/*
 *Name        : hynintron_ex_fun.c
 *Author      : steven
 *Version     : V2.0
 *Create      : 2019-11-11
 *Copyright   : zxzz
 */

#include "hynitron_update_firmware.h"
#include "hynitron_common.h"
#include "hynitron_core.h"
#include <linux/dcache.h>
#include <linux/delay.h>
#include <linux/file.h>
#include <linux/fs.h>
#include <linux/gpio.h>
#include <linux/i2c.h>
#include <linux/mount.h>
#include <linux/namei.h>
#include <linux/netdevice.h>
#include <linux/proc_fs.h>
#include <linux/string.h>

#include "firmware/capacitive_hynitron_cst0xx_update.h"
#include "firmware/capacitive_hynitron_cst1xxse_update.h"
#include "firmware/capacitive_hynitron_cst3240_update.h"
//#include "firmware/capacitive_hynitron_cst2xx_update.h"
#include "firmware/capacitive_hynitron_cst2xxse_update.h"
#include "firmware/capacitive_hynitron_cst3xx_update.h"
#include "firmware/capacitive_hynitron_cst3xxse_update.h"
#include "firmware/capacitive_hynitron_cst6xx_update.h"
#include "firmware/capacitive_hynitron_cst8xx_update.h"
#include "firmware/capacitive_hynitron_cst92xx_update.h"
#include "firmware/capacitive_hynitron_cst9xx_update.h"

// please config the chip series before using.
struct hynitron_fw_array hynitron_fw_grp[20] = {
	// 0-name; 1-fw; 2-project_id; 3-module_id; 4-chip_type; 5-fw_length;
	{"capacitive_hynitron_cst0xx_update", cst0xx_fw, 0x2843, 0x01, CST016,
	 (sizeof(cst0xx_fw))},
	{"capacitive_hynitron_cst2xxse_update", cst2xxse_fw, 0x0501, 0x01,
	 CST226SE, (sizeof(cst2xxse_fw))},
	{"capacitive_hynitron_cst1xxse_update", cst1xxse_fw, 0x2107, 0x01,
	 CST18858SE, (sizeof(cst1xxse_fw))},
	{"capacitive_hynitron_cst3xx_update", cst3xx_fw, 0x2117, 0x11, CST348,
	 (sizeof(cst3xx_fw))},
	{"capacitive_hynitron_cst3240_update", cst3240_fw, 0x3604, 0x01,
	 CST3240, (sizeof(cst3240_fw))},
	{"capacitive_hynitron_cst6xx_update", cst6xx_fw, 0x2117, 0x11, CST6928S,
	 (sizeof(cst6xx_fw))},
	{"capacitive_hynitron_cst3xxse_update", cst3xxse_fw, 0x0501, 0x01,
	 CST328SE, (sizeof(cst3xxse_fw))},
	{"capacitive_hynitron_cst8xx_update", cst8xx_fw, 0x0501, 0x01, CST836,
	 (sizeof(cst8xx_fw))},
	{"capacitive_hynitron_cst9xx_update", cst9xx_fw, 0x2208, 0x01, CST918,
	 (sizeof(cst9xx_fw))},
	{"capacitive_hynitron_cst92xx_update", cst92xx_fw, 0x2908, 0x01,
	 CST9217, (sizeof(cst92xx_fw))},

};

struct hynitron_chip_type_array hynitron_chip_type_grp[50] = {
	{CST016, HYN_CHIP_CST0XX, HYN_CHIP_PRODUCT_LINE_SEL_CAP, 0x15},
	{CST026, HYN_CHIP_CST0XX, HYN_CHIP_PRODUCT_LINE_SEL_CAP, 0x15},
	{CST036, HYN_CHIP_CST0XX, HYN_CHIP_PRODUCT_LINE_SEL_CAP, 0x15},

	{CST126, HYN_CHIP_CST1XX, HYN_CHIP_PRODUCT_LINE_MUT_CAP, 0x1A},
	{CST128, HYN_CHIP_CST1XX, HYN_CHIP_PRODUCT_LINE_MUT_CAP, 0x1A},
	{CST130, HYN_CHIP_CST1XX, HYN_CHIP_PRODUCT_LINE_MUT_CAP, 0x1A},
	{CST128SE, HYN_CHIP_CST1XXSE, HYN_CHIP_PRODUCT_LINE_MUT_CAP, 0x5A},
	{CST18858SE, HYN_CHIP_CST1XXSE, HYN_CHIP_PRODUCT_LINE_MUT_CAP, 0x5A},
	{CST140, HYN_CHIP_CST1XX, HYN_CHIP_PRODUCT_LINE_MUT_CAP, 0x1A},
	{CST148, HYN_CHIP_CST1XX, HYN_CHIP_PRODUCT_LINE_MUT_CAP, 0x1A},

	{CST226, HYN_CHIP_CST2XX, HYN_CHIP_PRODUCT_LINE_MUT_CAP, 0x1A},
	{CST226SE, HYN_CHIP_CST2XXSE, HYN_CHIP_PRODUCT_LINE_MUT_CAP, 0x5A},
	{CST237, HYN_CHIP_CST2XX, HYN_CHIP_PRODUCT_LINE_MUT_CAP, 0x1A},
	{CST240, HYN_CHIP_CST2XX, HYN_CHIP_PRODUCT_LINE_MUT_CAP, 0x1A},

	{CST326, HYN_CHIP_CST1XX, HYN_CHIP_PRODUCT_LINE_MUT_CAP, 0x1A},
	{CST328, HYN_CHIP_CST1XX, HYN_CHIP_PRODUCT_LINE_MUT_CAP, 0x1A},
	{CST328SE, HYN_CHIP_CST3XXSE, HYN_CHIP_PRODUCT_LINE_MUT_CAP, 0x5A},
	{CST340, HYN_CHIP_CST3XX, HYN_CHIP_PRODUCT_LINE_MUT_CAP, 0x1A},
	{CST348, HYN_CHIP_CST3XX, HYN_CHIP_PRODUCT_LINE_MUT_CAP, 0x1A},
	{CST356, HYN_CHIP_CST3XX, HYN_CHIP_PRODUCT_LINE_MUT_CAP, 0x1A},
	{CST3240, HYN_CHIP_CST3240, HYN_CHIP_PRODUCT_LINE_MUT_CAP, 0x5A},
	{CST6928S, HYN_CHIP_CST6XX, HYN_CHIP_PRODUCT_LINE_MUT_CAP, 0x1A},

	{CST716, HYN_CHIP_CST7XX, HYN_CHIP_PRODUCT_LINE_SEL_CAP, 0x15},
	{CST726, HYN_CHIP_CST7XX, HYN_CHIP_PRODUCT_LINE_SEL_CAP, 0x15},
	{CST736, HYN_CHIP_CST7XX, HYN_CHIP_PRODUCT_LINE_SEL_CAP, 0x15},

	{CST816, HYN_CHIP_CST8XX, HYN_CHIP_PRODUCT_LINE_SEL_CAP, 0x15},
	{CST826, HYN_CHIP_CST8XX, HYN_CHIP_PRODUCT_LINE_SEL_CAP, 0x15},
	{CST836, HYN_CHIP_CST8XX, HYN_CHIP_PRODUCT_LINE_SEL_CAP, 0x15},

	{CST912, HYN_CHIP_CST9XX, HYN_CHIP_PRODUCT_LINE_MUT_CAP, 0x1A},
	{CST918, HYN_CHIP_CST9XX, HYN_CHIP_PRODUCT_LINE_MUT_CAP, 0x1A},

	{CST9217, HYN_CHIP_CST92XX, HYN_CHIP_PRODUCT_LINE_MUT_CAP, 0x5A},
	{CST9220, HYN_CHIP_CST92XX, HYN_CHIP_PRODUCT_LINE_MUT_CAP, 0x5A},

	{CST644K, HYN_CHIP_CST644K, HYN_CHIP_PRODUCT_LINE_MUT_CAP, 0x5A},
};

int cst3xx_firmware_info(struct i2c_client *client);

static unsigned char update_fw_flag;
#if HYN_EN_AUTO_UPDATE

#if HYN_EN_AUTO_UPDATE_CST78xx
static unsigned char dev_addr;
static unsigned char chip_sumok_flag;
/*
 *
 */
static int cst78xx_enter_bootmode(void) {
	char retryCnt = 10;

	hyn_reset_proc(1);
	mdelay(5);
	while (retryCnt--) {
		u8 cmd[3];
		cmd[0] = 0xAA;
		if (-1 ==
		    hyn_i2c_write_bytes(0xA001, cmd, 1,
					REG_LEN_2B)) { // enter program mode
			mdelay(2);		       // 4ms
			continue;
		}
		if (-1 == hyn_i2c_read_bytes(0xA003, cmd, 1,
					     REG_LEN_2B)) { // read flag
			mdelay(2);			    // 4ms
			continue;
		} else {
			if (cmd[0] != 0x55) {
				msleep(2); // 4ms
				continue;
			} else {
				return 0;
			}
		}
	}
	return -1;
}
/*
 *
 */
static int cst78xx_update(u16 startAddr, u16 len, u8 *src) {
	u16 sum_len;
	u8 cmd[10];
	int ret;

	ret = 0;
	HYN_FUNC_ENTER();
	if (cst78xx_enter_bootmode() == -1) {
		return -1;
	}
	sum_len = 0;

#define PER_LEN 512
	do {
		if (sum_len >= len) {
			return -1;
		}

		// send address
		cmd[1] = startAddr >> 8;
		cmd[0] = startAddr & 0xFF;
		hyn_i2c_write_bytes(0xA014, cmd, 2, REG_LEN_2B);

#if HYN_IIC_TRANSFER_LIMIT
		{
			u8 temp_buf[8];
			u16 j, iic_addr;
			iic_addr = 0;
			for (j = 0; j < 128; j++) {

				temp_buf[0] = *((u8 *)src + iic_addr + 0);
				temp_buf[1] = *((u8 *)src + iic_addr + 1);
				temp_buf[2] = *((u8 *)src + iic_addr + 2);
				temp_buf[3] = *((u8 *)src + iic_addr + 3);

				hyn_i2c_write_bytes((0xA018 + iic_addr),
						    (u8 *)temp_buf, 4,
						    REG_LEN_2B);
				iic_addr += 4;
				if (iic_addr == 512)
					break;
			}
		}
#else
		hyn_i2c_write_bytes(0xA018, src, PER_LEN, REG_LEN_2B);
#endif
		cmd[0] = 0xEE;
		hyn_i2c_write_bytes(0xA004, cmd, 1, REG_LEN_2B);

		if (hyn_ts_data->apk_upgrade_flag == 0)
			msleep(300);
		else
			msleep(100);

		{
			u8 retrycnt = 50;
			while (retrycnt--) {
				cmd[0] = 0;
				hyn_i2c_read_bytes(0xA005, cmd, 1, REG_LEN_2B);
				if (cmd[0] == 0x55) {
					// success
					break;
				}
				msleep(10);
			}

			if (cmd[0] != 0x55) {
				ret = -1;
			}
		}
		startAddr += PER_LEN;
		src += PER_LEN;
		sum_len += PER_LEN;
	} while (len);

	// exit program mode
	cmd[0] = 0x00;
	hyn_i2c_write_bytes(0xA003, cmd, 1, REG_LEN_2B);
	HYN_FUNC_EXIT();
	return ret;
}
/*
 *
 */
static u32 cst78xx_read_checksum(u16 startAddr, u16 len) {
	union {
		u32 sum;
		u8 buf[4];
	} checksum;
	char cmd[3];
	char readback[4] = {0};

	if (cst78xx_enter_bootmode() == -1) {
		return -1;
	}

	cmd[0] = 0;
	if (-1 == hyn_i2c_write_bytes(0xA003, cmd, 1, REG_LEN_2B)) {
		return -1;
	}
	msleep(500);

	if (-1 == hyn_i2c_read_bytes(0xA000, readback, 1, REG_LEN_2B)) {
		return -1;
	}
	if (readback[0] != 1) {
		return -1;
	}
	if (-1 == hyn_i2c_read_bytes(0xA008, checksum.buf, 4, REG_LEN_2B)) {
		return -1;
	}
	chip_sumok_flag = 1;

	return checksum.sum;
}

int cst8xx_boot_update_fw(struct i2c_client *mclient) {
	unsigned short startAddr;
	unsigned short length;
	unsigned short checksum;
	unsigned short chipchecksum;

	HYN_FUNC_ENTER();
	update_fw_flag = 1;
	chip_sumok_flag = 0;

	hyn_ts_data->client = mclient;

	if (hyn_ts_data->apk_upgrade_flag == 0)
		cst8xx_firmware_info(mclient);

	dev_addr = hyn_ts_data->client->addr;

	hyn_ts_data->client->addr = 0x6A;

	if (cst78xx_enter_bootmode() == 0) {

		if ((hyn_ts_data->fw_length) > 10) {

			startAddr =
				*(hyn_ts_data->p_hynitron_upgrade_firmware + 1);

			length =
				*(hyn_ts_data->p_hynitron_upgrade_firmware + 3);

			checksum =
				*(hyn_ts_data->p_hynitron_upgrade_firmware + 5);

			startAddr <<= 8;
			startAddr |=
				*(hyn_ts_data->p_hynitron_upgrade_firmware + 0);

			length <<= 8;
			length |=
				*(hyn_ts_data->p_hynitron_upgrade_firmware + 2);

			checksum <<= 8;
			checksum |=
				*(hyn_ts_data->p_hynitron_upgrade_firmware + 4);

			chipchecksum = cst78xx_read_checksum(startAddr, length);

			if (update_fw_flag || chip_sumok_flag == 0) {
				HYN_INFO("\r\ncst8xx_boot_update_fw:  low "
					 "version,  updating!!!r\n");
				HYN_INFO("\r\n CTP cst78xx File, start-0x%04x "
					 "len-0x%04x "
					 "fileCheck-0x%04x\r\n",
					 startAddr, length, checksum);
				if (chipchecksum != checksum) {
					cst78xx_update(
						startAddr, length,
						(hyn_ts_data
							 ->p_hynitron_upgrade_firmware +
						 6));
					length = cst78xx_read_checksum(
						startAddr, length);
					HYN_INFO("\r\nCTP cst78xx update %s, "
						 "checksum-0x%04x",
						 ((length == checksum)
							  ? "success"
							  : "fail"),
						 length);

				} else {
					HYN_INFO("\r\nCTP cst78xx check "
						 "pass...");
				}
			} else {
				HYN_INFO("\r\ncst8xx_boot_update_fw:  high "
					 "version  not update!!!r\n");
			}
		}
		goto re;
	} else {
		hyn_ts_data->client->addr = dev_addr;
		return -1;
	}
	HYN_FUNC_EXIT();
re:
	hyn_ts_data->client->addr = dev_addr;
	hyn_reset_proc(10);
	return 0;
}

#endif

#if HYN_EN_AUTO_UPDATE_CST2xx

#define CST2XX_BASE_ADDR (24 * 1024)
#define CST2XX_FW_BIN_SIZE (6 * 1024)
static int cst2xx_enter_download_mode(void) {
	int ret;
	unsigned char buf[2];

	buf[0] = 0xD1;
	buf[1] = 0x11;
	ret = cst3xx_i2c_write(hyn_ts_data->client, buf, 2);
	if (ret < 0) {
		return -1;
	}

	mdelay(1); // wait enter download mode

	buf[0] = 0xD0;
	buf[1] = 0x01;
	ret = cst3xx_i2c_read_register(hyn_ts_data->client, buf, 1);
	if (ret < 0) {
		return -1;
	}
	if (buf[0] != 0x55) {
		HYN_DEBUG("[HYN]reciev 0x55 failed.\r\n");
		return -1;
	}

	buf[0] = 0xD1;
	buf[1] = 0x10; // enter writer register mode
	ret = cst3xx_i2c_write(hyn_ts_data->client, buf, 2);
	if (ret < 0) {
		HYN_DEBUG("[HYN]Send cmd 0xD110 failed. \r\n");
		return -1;
	}

	return 0;
}

static int cst2xx_download_program(unsigned char *data, int len) {
	int ret;
	int i, j;
	unsigned int wr_addr;
	unsigned char *pData;
	unsigned char *pSrc;
	unsigned char *pDst;
	unsigned char i2c_buf[8];

	pData = kmalloc(sizeof(unsigned char) * (1024 + 4), GFP_KERNEL);
	if (NULL == pData) {
		HYN_ERROR("malloc data buffer failed.\n");
		return -1;
	}

	pSrc = data;
	HYN_DEBUG("write program data begain:0x%x.\n", len);

	for (i = 0; i < (len / 1024); i++) {
		wr_addr = (i << 10) + CST2XX_BASE_ADDR;
		pData[0] = (wr_addr >> 24) & 0xFF;
		pData[1] = (wr_addr >> 16) & 0xFF;
		pData[2] = (wr_addr >> 8) & 0xFF;
		pData[3] = wr_addr & 0xFF;
		pDst = pData + 4;

		for (j = 0; j < 256; j++) {
			*pDst = *(pSrc + 3);
			*(pDst + 1) = *(pSrc + 2);
			*(pDst + 2) = *(pSrc + 1);
			*(pDst + 3) = *pSrc;
			pDst += 4;
			pSrc += 4;
		}

#if (!HYN_IIC_TRANSFER_LIMIT)
		for (j = 0; j < 256; j++) {
			i2c_buf[0] = (wr_addr >> 24) & 0xFF;
			i2c_buf[1] = (wr_addr >> 16) & 0xFF;
			i2c_buf[2] = (wr_addr >> 8) & 0xFF;
			i2c_buf[3] = wr_addr & 0xFF;
			i2c_buf[4] = pData[j * 4 + 4 + 0];
			i2c_buf[5] = pData[j * 4 + 4 + 1];
			i2c_buf[6] = pData[j * 4 + 4 + 2];
			i2c_buf[7] = pData[j * 4 + 4 + 3];
			ret = cst3xx_i2c_write(hyn_ts_data->client, i2c_buf, 8);
			if (ret < 0) {
				HYN_ERROR("program failed.\n");
				goto ERR_OUT;
			}
			wr_addr += 4;
		}
#else
		ret = cst3xx_i2c_write(hyn_ts_data->client, pData, 1024 + 4);
		if (ret < 0) {
			HYN_ERROR("program failed.\n");
			goto ERR_OUT;
		}

#endif
	}

	// clear update key
	pData[3] = 0x20000FFC & 0xFF;
	pData[2] = (0x20000FFC >> 8) & 0xFF;
	pData[1] = (0x20000FFC >> 16) & 0xFF;
	pData[0] = (0x20000FFC >> 24) & 0xFF;
	pData[4] = 0x00;
	pData[5] = 0x00;
	pData[6] = 0x00;
	pData[7] = 0x00;
	ret = cst3xx_i2c_write(hyn_ts_data->client, pData, 8);
	if (ret < 0) {
		HYN_ERROR("clear update key failed.\n");
		goto ERR_OUT;
	}

	pData[3] = 0xD013D013 & 0xFF;
	pData[2] = (0xD013D013 >> 8) & 0xFF;
	pData[1] = (0xD013D013 >> 16) & 0xFF;
	pData[0] = (0xD013D013 >> 24) & 0xFF;
	ret = cst3xx_i2c_write(hyn_ts_data->client, pData, 4);
	if (ret < 0) {
		HYN_ERROR("exit register read/write mode failed.\n");
		goto ERR_OUT;
	}

	HYN_DEBUG("--------write program data end--------.\r\n");

	if (pData != NULL) {
		kfree(pData);
		pData = NULL;
	}

	return 0;

ERR_OUT:
	if (pData != NULL) {
		kfree(pData);
		pData = NULL;
	}

	return -1;
}

static int cst2xx_read_checksum(void) {
	int ret;
	int i;
	unsigned int checksum;
	unsigned int bin_checksum;
	unsigned char buf[4];
	unsigned char *pData;

	for (i = 0; i < 10; i++) {
		buf[0] = 0xD0;
		buf[1] = 0x00;
		ret = cst3xx_i2c_read_register(hyn_ts_data->client, buf, 1);
		if (ret < 0) {
			mdelay(2);
			continue;
		}

		if ((buf[0] == 0x01) || (buf[0] == 0x02))
			break;

		mdelay(2);
	}

	if ((buf[0] == 0x01) || (buf[0] == 0x02)) {
		buf[0] = 0xD0;
		buf[1] = 0x08;
		ret = cst3xx_i2c_read_register(hyn_ts_data->client, buf, 4);
		if (ret < 0)
			return -1;

		// handle read data  --> checksum
		checksum = buf[0] + (buf[1] << 8) + (buf[2] << 16) +
			   (buf[3] << 24);
		pData = hyn_ts_data->p_hynitron_upgrade_firmware +
			6160; // 6*1024+16
		bin_checksum = pData[0] + (pData[1] << 8) + (pData[2] << 16) +
			       (pData[3] << 24);
		if (checksum != bin_checksum)
			HYN_DEBUG("Check sum error.\n");

		HYN_DEBUG("checksum ic:0x%x. bin:0x%x-----\n", checksum,
			  bin_checksum);

		buf[0] = 0xD0;
		buf[1] = 0x01;
		buf[2] = 0xA5;
		ret = cst3xx_i2c_write(hyn_ts_data->client, buf, 3);
		if (ret < 0)
			return -1;
	} else {
		HYN_DEBUG("No checksum.\n");
		return -1;
	}

	return 0;
}

static int cst2xx_update_firmware(struct i2c_client *client,
				  unsigned char *pdata, int data_len) {
	int ret;
	int retry;
	retry = 0;
	HYN_DEBUG("enter the update firmware.\n");
start_flow:

	client->addr = hyn_ts_data->chip_ic_main_addr;
	hyn_reset_proc(5 + retry);
	client->addr = hyn_ts_data->chip_ic_bootloader_addr;
	hyn_ts_data->fw_updating = 1;
	ret = cst2xx_enter_download_mode();
	if (ret < 0) {
		HYN_ERROR("enter download mode failed.\n");
		goto fail_retry;
	}
	hyn_ts_data->fw_updating = 2;
	ret = cst2xx_download_program(pdata, data_len);
	if (ret < 0) {
		HYN_ERROR("download program failed.\n");
		goto fail_retry;
	}

	mdelay(3);
	hyn_ts_data->fw_updating = 3;
	ret = cst2xx_read_checksum();
	if (ret < 0) {
		HYN_ERROR("checksum failed.\n");
		goto fail_retry;
	}
	client->addr = hyn_ts_data->chip_ic_main_addr;

	HYN_DEBUG("Download firmware succesfully.\n");

	return 0;

fail_retry:
	if (retry < 20) {
		retry++;
		mdelay(20);
		goto start_flow;
	} else {
		client->addr = hyn_ts_data->chip_ic_main_addr;
	}
	return -1;
}

static int cst2xx_boot_update_fw(struct i2c_client *client) {
	return cst2xx_update_firmware(client,
				      hyn_ts_data->p_hynitron_upgrade_firmware,
				      CST2XX_FW_BIN_SIZE);
}

#endif

#if (HYN_EN_AUTO_UPDATE_CST3xxSE || HYN_EN_AUTO_UPDATE_CST2xxSE ||             \
     HYN_EN_AUTO_UPDATE_CST1xxSE)

#define CST3XXSE_BASE_ADDR (0x00000000)
#define CST3XXSE_FW_BIN_SIZE (7 * 1024 + 512)

static int cst3xxse_enter_download_mode(void) {
	int ret;
	unsigned char buf[4];

	buf[0] = 0xA0;
	buf[1] = 0x01;
	buf[2] = 0xAA;
	ret = cst3xx_i2c_write(hyn_ts_data->client, buf, 3);
	if (ret < 0) {
		return -1;
	}
	mdelay(5); // wait enter download mode

	buf[0] = 0xA0;
	buf[1] = 0x03; // check whether into program mode
	ret = cst3xx_i2c_read_register(hyn_ts_data->client, buf, 1);
	if (ret < 0) {
		return -1;
	}
	if (buf[0] != 0x55) {
		HYN_ERROR("hyn reciev 0x55 failed.\n");
		return -1;
	}

	return 0;
}

static int cst3xxse_download_program(unsigned char *pdata, int len) {
	int i, ret, j, retry;
	unsigned char *i2c_buf;
	unsigned char temp_buf[8];
	unsigned short eep_addr, iic_addr;
	int total_kbyte;

	i2c_buf = kmalloc(sizeof(unsigned char) * (512 + 2), GFP_KERNEL);
	if (i2c_buf == NULL) {
		return -1;
	}

	// make sure fwbin len is N*1K

	total_kbyte = len / 512;

	for (i = 0; i < total_kbyte; i++) {
		i2c_buf[0] = 0xA0;
		i2c_buf[1] = 0x14;
		eep_addr = i << 9; // i * 512
		i2c_buf[2] = eep_addr;
		i2c_buf[3] = eep_addr >> 8;
		ret = cst3xx_i2c_write(hyn_ts_data->client, i2c_buf, 4);
		if (ret < 0)
			goto error_out;

#if (!HYN_IIC_TRANSFER_LIMIT)
		i2c_buf[0] = 0xA0;
		i2c_buf[1] = 0x18;
		memcpy(i2c_buf + 2, pdata + eep_addr, 512);
		ret = cst3xx_i2c_write(hyn_ts_data->client, i2c_buf, 514);
		if (ret < 0)
			goto error_out;
#else

		memcpy(i2c_buf, pdata + eep_addr, 512);
		for (j = 0; j < 128; j++) {
			iic_addr = (j << 2);
			temp_buf[0] = (iic_addr + 0xA018) >> 8;
			temp_buf[1] = (iic_addr + 0xA018) & 0xFF;
			temp_buf[2] = i2c_buf[iic_addr + 0];
			temp_buf[3] = i2c_buf[iic_addr + 1];
			temp_buf[4] = i2c_buf[iic_addr + 2];
			temp_buf[5] = i2c_buf[iic_addr + 3];
			ret = cst3xx_i2c_write(hyn_ts_data->client, temp_buf,
					       6);
			if (ret < 0)
				goto error_out;
		}
#endif

		i2c_buf[0] = 0xA0;
		i2c_buf[1] = 0x04;
		i2c_buf[2] = 0xEE;
		ret = cst3xx_i2c_write(hyn_ts_data->client, i2c_buf, 3);
		if (ret < 0)
			goto error_out;

		mdelay(100); // 60

		for (retry = 0; retry < 10; retry++) {
			i2c_buf[0] = 0xA0;
			i2c_buf[1] = 0x05;
			ret = cst3xx_i2c_read_register(hyn_ts_data->client,
						       i2c_buf, 1);

			if (ret < 0) {
				mdelay(100);
				continue;
			} else {
				if (i2c_buf[0] != 0x55) {
					mdelay(100);
					continue;
				} else {
					break;
				}
			}
		}
		if (retry == 10) {

			goto error_out;
		}
	}

	i2c_buf[0] = 0xA0;
	i2c_buf[1] = 0x01;
	i2c_buf[2] = 0x00;
	ret = cst3xx_i2c_write(hyn_ts_data->client, i2c_buf, 3);
	if (ret < 0)
		goto error_out;

	i2c_buf[0] = 0xA0;
	i2c_buf[1] = 0x03;
	i2c_buf[2] = 0x00;
	ret = cst3xx_i2c_write(hyn_ts_data->client, i2c_buf, 3);
	if (ret < 0)
		goto error_out;

	if (i2c_buf != NULL) {
		kfree(i2c_buf);
		i2c_buf = NULL;
	}

	return 0;

error_out:
	if (i2c_buf != NULL) {
		kfree(i2c_buf);
		i2c_buf = NULL;
	}
	return -1;
}

static int cst3xxse_read_checksum(unsigned char *p_fw) {
	int ret;
	int i;
	unsigned int checksum;
	unsigned int bin_checksum;
	unsigned char buf[4];
	const unsigned char *pData;

	for (i = 0; i < 10; i++) {
		buf[0] = 0xA0;
		buf[1] = 0x00;
		ret = cst3xx_i2c_read_register(hyn_ts_data->client, buf, 1);
		if (ret < 0) {
			mdelay(2);
			continue;
		}

		if (buf[0] != 0)
			break;
		else
			mdelay(2);
	}
	mdelay(4);

	if (buf[0] == 0x01) {
		buf[0] = 0xA0;
		buf[1] = 0x08;
		ret = cst3xx_i2c_read_register(hyn_ts_data->client, buf, 4);

		if (ret < 0)
			return -1;
		checksum = buf[0] + (buf[1] << 8) + (buf[2] << 16) +
			   (buf[3] << 24);
		pData = (unsigned char *)p_fw + 7680 - 4; // 7*1024 +512
		bin_checksum = pData[0] + (pData[1] << 8) + (pData[2] << 16) +
			       (pData[3] << 24);

		HYN_INFO("hyn checksum ic:0x%x. bin:0x%x------\n", checksum,
			 bin_checksum);
		if (checksum != bin_checksum) {
			HYN_ERROR("hyn check sum error.\n");
			return -1;
		}

	} else {
		HYN_ERROR("hyn No checksum. buf[0]:%d.\n", buf[0]);
		return -1;
	}

	return 0;
}

static int cst3xxse_update_firmware(struct i2c_client *client,
				    unsigned char *pdata) {
	int ret;
	int retry;
	unsigned char buf[4];

	retry = 0;
	HYN_INFO("hyn cst3xxse_update_firmware enter.\n");
	mdelay(20);

start_flow:

	client->addr = hyn_ts_data->chip_ic_main_addr;
	hyn_reset_proc(5 + retry);
	client->addr = hyn_ts_data->chip_ic_bootloader_addr;
	hyn_ts_data->fw_updating = 1;
	ret = cst3xxse_enter_download_mode();
	if (ret < 0) {
		HYN_ERROR("hyn cst3xxse_enter_download_mode enter failed.\n");
		goto fail_retry;
	}

	hyn_ts_data->fw_updating = 2;
	ret = cst3xxse_download_program(pdata, CST3XXSE_FW_BIN_SIZE);
	if (ret < 0) {
		HYN_ERROR("hyn cst3xxse_download_program failed.\n");
		goto fail_retry;
	}

	mdelay(5);
	hyn_ts_data->fw_updating = 3;
	ret = cst3xxse_read_checksum(pdata);
	if (ret < 0) {
		HYN_ERROR("hyn cst3xxse_read_checksum failed.\n");
		goto fail_retry;
	} else {
		buf[0] = 0xA0; // exit program
		buf[1] = 0x06;
		buf[2] = 0xEE;
		ret = cst3xx_i2c_write(client, buf, 3);

		// if(ret < 0)
		//	goto fail_retry;;
	}

	HYN_INFO("hyn cst3xxse_update_firmware succesfully.\n");

	mdelay(10);
	client->addr = hyn_ts_data->chip_ic_main_addr;

	hyn_reset_proc(20);

	return 0;

fail_retry:
	if (retry < 30) {
		retry++;
		mdelay(20);
		goto start_flow;
	} else {
		client->addr = hyn_ts_data->chip_ic_main_addr;
	}

	return -1;
}
static int cst3xxse_update_judge(unsigned char *fw, int strict) {
	unsigned short ic_type, project_id;
	unsigned int bin_checksum, bin_version;
	unsigned char *pData;
	unsigned int *pData_check;
	int i;

	bin_checksum = 0x55;
	pData_check = (unsigned int *)fw;
	for (i = 0; i < (CST3XXSE_FW_BIN_SIZE - 4); i += 4) {
		bin_checksum += (*pData_check);
		pData_check++;
	}

	if (bin_checksum != (*pData_check)) {
		HYN_ERROR("calculated checksum error:0x%x not equal 0x%x.\n",
			  bin_checksum, *pData_check);
		return -1; // bad fw, so do not update
	}
	// checksum

	pData = (unsigned char *)fw + 7680 - 4; // 7*1024 +512
	bin_checksum = pData[0] + (pData[1] << 8) + (pData[2] << 16) +
		       (pData[3] << 24);

	pData = (unsigned char *)fw + 7680 - 8; // 7*1024 +512
	bin_version = pData[0] + (pData[1] << 8) + (pData[2] << 16) +
		      (pData[3] << 24);

	pData = (unsigned char *)fw + 7680 - 12; // 7*1024 +512
	project_id = pData[1];
	project_id <<= 8;
	project_id |= pData[0];

	ic_type = pData[3];
	ic_type <<= 8;
	ic_type |= pData[2];

	if (strict > 0) {

		if (hyn_ts_data->chip_ic_checksum != bin_checksum) {
			HYN_ERROR("hyn checksum is "
				  "different******bin_checksum:0x%x, "
				  "g_cst3xx_ic_checksum:0x%x. \r\n",
				  bin_checksum, hyn_ts_data->chip_ic_checksum);
#if HYN_UPDATE_FIRMWARE_FORCE
			HYN_INFO("update firmware online force.\n");
			return 0;
#endif

			if (hyn_ts_data->chip_ic_checkcode == 0xffffffff) {
				HYN_ERROR("chip ic have no firmware, start "
					  "update config firmware.\n");
				return 0;
			} else if ((hyn_ts_data->chip_ic_type != ic_type) ||
				   (hyn_ts_data->chip_ic_project_id !=
				    project_id)) {

				HYN_ERROR("chip_ic_type:0x%04x, "
					  "chip_ic_project_id:0x%04x, not "
					  "match.\n",
					  hyn_ts_data->chip_ic_type,
					  hyn_ts_data->chip_ic_project_id);
				return -1;
			}

			// chip version
			pData = (unsigned char *)fw + 7680 - 8; // 7*1024 +512
			bin_version = pData[0] + (pData[1] << 8) +
				      (pData[2] << 16) + (pData[3] << 24);

			HYN_INFO("hyn bin_version is "
				 "different******bin_version:0x%x, "
				 "fw_version:0x%x. \r\n",
				 bin_version, hyn_ts_data->chip_ic_fw_version);

			mdelay(5);

			if (bin_version < hyn_ts_data->chip_ic_fw_version) {
				HYN_ERROR("hyn g_cst3xx_ic_version is higher , "
					  "no need to update "
					  "firmware.\n");
				return -1;
			}
		} else {
			HYN_ERROR("bin_checksum(0x%x), "
				  "g_cst3xx_ic_checksum(0x%x).\n",
				  bin_checksum, hyn_ts_data->chip_ic_checksum);
			return -1;
		}
	}
	return 0;
}
static int cst3xxse_boot_update_fw(struct i2c_client *client) {
	int ret = -1;

	if (hyn_ts_data->apk_upgrade_flag == 0) {
		ret = cst3xxse_update_judge(
			hyn_ts_data->p_hynitron_upgrade_firmware, 1);
		if (ret < 0) {
			HYN_ERROR("cst3xxse_update_judge fail , "
				  "apk_upgrade_flag = %d.\n",
				  hyn_ts_data->apk_upgrade_flag);
			return 0;
		}
	} else {
		ret = cst3xxse_update_judge(
			hyn_ts_data->p_hynitron_upgrade_firmware, 0);
		if (ret < 0) {
			HYN_ERROR("cst3xxse_update_judge fail , "
				  "apk_upgrade_flag = %d.\n",
				  hyn_ts_data->apk_upgrade_flag);
			return 0;
		}
	}
	return cst3xxse_update_firmware(
		client, hyn_ts_data->p_hynitron_upgrade_firmware);
}

#endif

#if (HYN_EN_AUTO_UPDATE_CST6xx)

/*******************************************************
Function:
	read checksum in bootloader mode
Input:
	client: i2c client
	strict: check checksum value
Output:
	success: 0
	fail:	-1
*******************************************************/

#define CST6XX_BIN_SIZE (24 * 1024 + 28)

static int cst6xx_check_checksum(struct i2c_client *client) {
	int ret;
	int i;
	unsigned int checksum;
	unsigned int bin_checksum;
	unsigned char buf[4];
	const unsigned char *pData;

	for (i = 0; i < 5; i++) {
		buf[0] = 0xA0;
		buf[1] = 0x00;
		ret = cst3xx_i2c_read_register(client, buf, 1);
		if (ret < 0) {
			mdelay(2);
			continue;
		}

		if (buf[0] != 0)
			break;
		else
			mdelay(2);
	}
	mdelay(2);

	if (buf[0] == 0x01) {
		buf[0] = 0xA0;
		buf[1] = 0x08;
		ret = cst3xx_i2c_read_register(client, buf, 4);

		if (ret < 0)
			return -1;

		// read chip checksum
		checksum = buf[0] + (buf[1] << 8) + (buf[2] << 16) +
			   (buf[3] << 24);
		pData = (unsigned char *)
				hyn_ts_data->p_hynitron_upgrade_firmware +
			24 * 1024 + 16 + 4; // 7*1024 +512
		bin_checksum = pData[0] + (pData[1] << 8) + (pData[2] << 16) +
			       (pData[3] << 24);

		HYN_INFO(" the updated ic checksum is :0x%x. the updating "
			 "firmware "
			 "checksum is:0x%x------\n",
			 checksum, bin_checksum);

		if (checksum != bin_checksum) {
			HYN_ERROR("hyn check sum error.\n");
			return -1;
		}

	} else {
		HYN_INFO("hyn No checksum.\n");
		return -1;
	}
	return 0;
}
static int cst6xx_into_program_mode(struct i2c_client *client) {
	int ret;
	unsigned char buf[4];

	buf[0] = 0xA0;
	buf[1] = 0x01;
	buf[2] = 0xAB; // set cmd to enter program mode
	ret = cst3xx_i2c_write(client, buf, 3);
	if (ret < 0)
		return -1;
	mdelay(2);

	buf[0] = 0xA0;
	buf[1] = 0x02; // check whether into program mode
	ret = cst3xx_i2c_read_register(client, buf, 1);
	if (ret < 0)
		return -1;

	if (buf[0] != 0x55)
		return -1;

	return 0;
}

static int cst6xx_exit_program_mode(struct i2c_client *client) {
	int ret;
	unsigned char buf[3];

	buf[0] = 0xA0;
	buf[1] = 0x06;
	buf[2] = 0xEE;
	ret = cst3xx_i2c_write(client, buf, 3);
	if (ret < 0)
		return -1;

	mdelay(10); // wait for restart

	return 0;
}

static int cst6xx_erase_program_area(struct i2c_client *client) {
	int ret;
	unsigned char buf[3];

	buf[0] = 0xA0;
	buf[1] = 0x02;
	buf[2] = 0x00; // set cmd to erase main area
	ret = cst3xx_i2c_write(client, buf, 3);
	if (ret < 0)
		return -1;

	mdelay(5);

	buf[0] = 0xA0;
	buf[1] = 0x03;
	ret = cst3xx_i2c_read_register(client, buf, 1);
	if (ret < 0)
		return -1;

	if (buf[0] != 0x55)
		return -1;

	return 0;
}

static int cst6xx_write_program_data(struct i2c_client *client,
				     const unsigned char *pdata) {
	int i, ret;
	unsigned char *i2c_buf;
	unsigned short eep_addr;
	int total_kbyte;
#if HYN_IIC_TRANSFER_LIMIT
	unsigned char temp_buf[8];
	unsigned short iic_addr;
	int j;

#endif

	i2c_buf = kmalloc(sizeof(unsigned char) * (1024 + 2), GFP_KERNEL);
	if (i2c_buf == NULL)
		return -1;

	// make sure fwbin len is N*1K
	// total_kbyte = len / 1024;

	total_kbyte = 24;
	for (i = 0; i < total_kbyte; i++) {
		i2c_buf[0] = 0xA0;
		i2c_buf[1] = 0x14;
		eep_addr = i << 10; // i * 1024
		i2c_buf[2] = eep_addr;
		i2c_buf[3] = eep_addr >> 8;
		ret = cst3xx_i2c_write(client, i2c_buf, 4);
		if (ret < 0)
			goto error_out;

#if HYN_IIC_TRANSFER_LIMIT
		memcpy(i2c_buf, pdata + eep_addr, 1024);
		for (j = 0; j < 256; j++) {
			iic_addr = (j << 2);
			temp_buf[0] = (iic_addr + 0xA018) >> 8;
			temp_buf[1] = (iic_addr + 0xA018) & 0xFF;
			temp_buf[2] = i2c_buf[iic_addr + 0];
			temp_buf[3] = i2c_buf[iic_addr + 1];
			temp_buf[4] = i2c_buf[iic_addr + 2];
			temp_buf[5] = i2c_buf[iic_addr + 3];
			ret = cst3xx_i2c_write(client, temp_buf, 6);
			if (ret < 0)
				goto error_out;
		}
#else

		i2c_buf[0] = 0xA0;
		i2c_buf[1] = 0x18;
		memcpy(i2c_buf + 2, pdata + eep_addr, 1024);
		ret = cst3xx_i2c_write(client, i2c_buf, 1026);
		if (ret < 0)
			goto error_out;
#endif

		i2c_buf[0] = 0xA0;
		i2c_buf[1] = 0x04;
		i2c_buf[2] = 0xEE;
		ret = cst3xx_i2c_write(client, i2c_buf, 3);
		if (ret < 0)
			goto error_out;

		mdelay(60);

		i2c_buf[0] = 0xA0;
		i2c_buf[1] = 0x05;
		ret = cst3xx_i2c_read_register(client, i2c_buf, 1);
		if (ret < 0)
			goto error_out;

		if (i2c_buf[0] != 0x55)
			goto error_out;
	}

	i2c_buf[0] = 0xA0;
	i2c_buf[1] = 0x03;
	i2c_buf[2] = 0x00;
	ret = cst3xx_i2c_write(client, i2c_buf, 3);
	if (ret < 0)
		goto error_out;

	mdelay(8);

	if (i2c_buf != NULL) {
		kfree(i2c_buf);
		i2c_buf = NULL;
	}

	return 0;

error_out:
	if (i2c_buf != NULL) {
		kfree(i2c_buf);
		i2c_buf = NULL;
	}
	return -1;
}

static int cst6xx_update_firmware(struct i2c_client *client,
				  const unsigned char *pdata) {
	int ret;
	int retry_timer = 0;
	int retry = 0;

	HYN_INFO("----------upgrade cst3xx begain------------\n");
	mdelay(20);

START_FLOW:
	hyn_reset_proc(5 + retry_timer);
	client->addr = hyn_ts_data->chip_ic_bootloader_addr;
	hyn_ts_data->fw_updating = 1;
	ret = cst6xx_into_program_mode(client);
	if (ret < 0) {
		HYN_ERROR("into program mode failed.\n");
		if (retry_timer < 20)
			retry_timer++;
		goto err_out;
	}
	hyn_ts_data->fw_updating = 2;
	ret = cst6xx_erase_program_area(client);
	if (ret < 0) {
		HYN_ERROR("erase main area failed.\n");
		goto err_out;
	}
	hyn_ts_data->fw_updating = 3;
	ret = cst6xx_write_program_data(client, pdata);
	if (ret < 0) {
		HYN_ERROR("write program data into cstxxx failed.\n");
		goto err_out;
	}
	hyn_ts_data->fw_updating = 4;
	ret = cst6xx_check_checksum(client);
	if (ret < 0) {
		HYN_ERROR(
			"after write program cst6xx_check_checksum failed.\n");
		goto err_out;
	}
	hyn_ts_data->fw_updating = 5;
	ret = cst6xx_exit_program_mode(client);
	if (ret < 0) {
		HYN_ERROR("exit program mode failed.\n");
		// goto err_out;
	}
	client->addr = hyn_ts_data->chip_ic_main_addr;
	hyn_reset_proc(30);

	hyn_ts_data->fw_updating = 6;
	HYN_INFO(" hyn----------cst3xx_update_firmware  end------------\n");

	return 0;

err_out:
	if (retry < 30) {
		retry++;
		mdelay(30);
		goto START_FLOW;
	} else {
		client->addr = hyn_ts_data->chip_ic_main_addr;
		return -1;
	}
}

static int cst6xx_update_judge(unsigned char *pdata, int strict) {
	unsigned short ic_type, project_id;
	unsigned int fw_checksum, fw_version;
	const unsigned int *p;
	int i;
	unsigned char *pBuf;

	fw_checksum = 0x55;
	p = (const unsigned int *)pdata;
	for (i = 0; i < (CST6XX_BIN_SIZE - 4); i += 4) {
		fw_checksum += (*p);
		p++;
	}

	if (fw_checksum != (*p)) {
		HYN_ERROR("calculated checksum error:0x%x not equal 0x%x.\n",
			  fw_checksum, *p);
		return -1; // bad fw, so do not update
	}

	pBuf = &pdata[CST6XX_BIN_SIZE - 16];

	project_id = pBuf[1];
	project_id <<= 8;
	project_id |= pBuf[0];

	ic_type = pBuf[3];
	ic_type <<= 8;
	ic_type |= pBuf[2];

	fw_version = pBuf[7];
	fw_version <<= 8;
	fw_version |= pBuf[6];
	fw_version <<= 8;
	fw_version |= pBuf[5];
	fw_version <<= 8;
	fw_version |= pBuf[4];

	fw_checksum = pBuf[11];
	fw_checksum <<= 8;
	fw_checksum |= pBuf[10];
	fw_checksum <<= 8;
	fw_checksum |= pBuf[9];
	fw_checksum <<= 8;
	fw_checksum |= pBuf[8];

	HYN_INFO("the updating firmware:project_id:0x%04x, ic type:0x%04x, "
		 "fw_version:0x%x, checksum:0x%x\n",
		 project_id, ic_type, fw_version, fw_checksum);

#if HYN_UPDATE_FIRMWARE_FORCE
	HYN_INFO("update firmware online force.\n");
	return 0;
#endif

	if (hyn_ts_data->chip_ic_checkcode == 0xffffffff) {
		HYN_ERROR("chip ic have no firmware, start update config "
			  "firmware.\n");
		return 0;
	} else if ((hyn_ts_data->chip_ic_type != ic_type) ||
		   ((hyn_ts_data->chip_ic_project_id != project_id) &&
		    (hyn_ts_data->apk_upgrade_flag == 0))) {

		HYN_ERROR("chip_ic_type:0x%04x, chip_ic_project_id:0x%04x, not "
			  "match.\n",
			  hyn_ts_data->chip_ic_type,
			  hyn_ts_data->chip_ic_project_id);
		return -1;
	}

	if (strict > 0) {
		if (hyn_ts_data->chip_ic_checksum != fw_checksum) {

			if (hyn_ts_data->chip_ic_fw_version > fw_version) {
				HYN_ERROR("fw version(%d), ic version(%d).\n",
					  fw_version,
					  hyn_ts_data->chip_ic_fw_version);
				return -1;
			}
		} else {
			HYN_ERROR("fw checksum(0x%x), ic checksum(0x%x).\n",
				  fw_checksum, hyn_ts_data->chip_ic_checksum);
			return -1;
		}
	}

	return 0;
}

static int cst6xx_boot_update_fw(struct i2c_client *client) {
	int ret;

	HYN_FUNC_ENTER();

	if (hyn_ts_data->apk_upgrade_flag == 0)
		ret = cst6xx_update_judge(
			hyn_ts_data->p_hynitron_upgrade_firmware, 1);
	else
		ret = cst6xx_update_judge(
			hyn_ts_data->p_hynitron_upgrade_firmware, 0);
	if (ret < 0) {
		HYN_ERROR(" update_judge fail, no need to update firmware.\n");
		return 0;
	}

	ret = cst6xx_update_firmware(client,
				     hyn_ts_data->p_hynitron_upgrade_firmware);
	if (ret < 0) {
		HYN_ERROR("update firmware failed, fw_updating:%d .\n",
			  hyn_ts_data->fw_updating);
		return -1;
	}
	hyn_ts_data->fw_updating = 0;

	mdelay(50);

	ret = cst3xx_firmware_info(client);
	if (ret < 0) {
		HYN_ERROR(" after update read version and checksum fail.\n");
		return -1;
	}

	HYN_FUNC_EXIT();
	return 0;
}
#endif

#if HYN_EN_AUTO_UPDATE_CST644K

#define CST644K_BIN_LEN ((1024 * 16 - 32))
static int cst644k_into_program_mode(struct i2c_client *client) {
	int ret;
	unsigned char buf[4];

	buf[0] = 0xA0;
	buf[1] = 0x01;
	buf[2] = 0xAA; // set cmd to enter program mode
	ret = cst3xx_i2c_write(client, buf, 3);
	if (ret < 0)
		return -1;
	mdelay(2);

	buf[0] = 0xA0;
	buf[1] = 0x02; // check whether into program mode
	ret = cst3xx_i2c_read_register(client, buf, 1);
	if (ret < 0)
		return -1;

	if (buf[0] != 0x55)
		return -1;

	return 0;
}

static int cst644k_write_program_data(struct i2c_client *client,
				      const unsigned char *pdata) {
	int i, ret;
	unsigned char *i2c_buf;
	int addr = 0;
	int bin_len = CST644K_BIN_LEN;
	int prog_addr = 0, prog_len = 0;
	int CODE_RDADDR = 0x0C;	 // pg len
	int CODE_RDTYPE = 0x10;	 // pg type
	int CODE_WRADDR = 0x14;	 // pg dst addr
	int CODEBUF_ADDR = 0x18; // pg src start

	i2c_buf = kmalloc(sizeof(unsigned char) * (580), GFP_KERNEL);
	if (i2c_buf == NULL)
		return -1;

	// 1st program last 512 - 32
	prog_len = CST644K_BIN_LEN % 512;
	prog_addr = CST644K_BIN_LEN - prog_len;

	i2c_buf[0] = 0xA0;
	i2c_buf[1] = CODE_RDTYPE;
	i2c_buf[2] = 0x02;
	cst3xx_i2c_write(client, i2c_buf, 3); // write type = 2

	i2c_buf[0] = 0xA0;
	i2c_buf[1] = CODE_RDADDR;
	i2c_buf[2] = (prog_len)&0xff;
	i2c_buf[3] = (prog_len >> 8) & 0xff;
	cst3xx_i2c_write(client, i2c_buf, 4); // write prog len

	i2c_buf[0] = 0xA0;
	i2c_buf[1] = CODE_WRADDR;
	i2c_buf[2] = (prog_addr)&0xff;
	i2c_buf[3] = (prog_addr >> 8) & 0xff;
	cst3xx_i2c_write(client, i2c_buf, 4); // write start addr

	i2c_buf[0] = 0xA0;
	i2c_buf[1] = CODEBUF_ADDR;
	memcpy(i2c_buf + 2, pdata + prog_addr, prog_len);
	cst3xx_i2c_write(client, i2c_buf, prog_len + 2); // write program data

	i2c_buf[0] = 0xA0;
	i2c_buf[1] = 0x04;
	i2c_buf[2] = 0xEE;
	cst3xx_i2c_write(client, i2c_buf, 3); // start prog

	for (i = 0; i < 5; i++) {
		mdelay(100);

		i2c_buf[0] = 0xA0;
		i2c_buf[1] = 0x05;
		ret = cst3xx_i2c_read_register(client, i2c_buf, 1);

		if (i2c_buf[0] == 0x55)
			break;
	}

	if (ret < 0 || (i2c_buf[0] != 0x55))
		goto error_out;

	i2c_buf[0] = 0xA0;
	i2c_buf[1] = CODE_RDTYPE;
	i2c_buf[2] = 0x00;
	cst3xx_i2c_write(client, i2c_buf, 3); // write type = 0

	for (addr = 0; addr < (bin_len - prog_len); addr += 512) {
		i2c_buf[0] = 0xA0;
		i2c_buf[1] = CODE_WRADDR;
		i2c_buf[2] = (addr)&0xff;
		i2c_buf[3] = (addr >> 8) & 0xff;
		cst3xx_i2c_write(client, i2c_buf, 4); // write start addr

		i2c_buf[0] = 0xA0;
		i2c_buf[1] = CODEBUF_ADDR;
		memcpy(i2c_buf + 2, pdata + addr, 512);
		cst3xx_i2c_write(client, i2c_buf, 514); // write program data

		i2c_buf[0] = 0xA0;
		i2c_buf[1] = 0x04;
		i2c_buf[2] = 0xEE;
		cst3xx_i2c_write(client, i2c_buf, 3); // start prog

		for (i = 0; i < 5; i++) {
			mdelay(100);

			i2c_buf[0] = 0xA0;
			i2c_buf[1] = 0x05;
			ret = cst3xx_i2c_read_register(client, i2c_buf, 1);

			if (i2c_buf[0] == 0x55)
				break;
		}

		if (ret < 0 || i2c_buf[0] != 0x55)
			goto error_out;
	}

	i2c_buf[0] = 0xA0;
	i2c_buf[1] = 0x03;
	i2c_buf[2] = 0x00;
	ret = cst3xx_i2c_write(client, i2c_buf, 3);
	if (ret < 0)
		goto error_out;

	mdelay(8);

	if (i2c_buf != NULL) {
		kfree(i2c_buf);
		i2c_buf = NULL;
	}

	return 0;

error_out:
	if (i2c_buf != NULL) {
		kfree(i2c_buf);
		i2c_buf = NULL;
	}
	return -1;
}

static int cst644k_check_checksum(struct i2c_client *client) {
	int ret;
	int i;
	unsigned int checksum;
	unsigned int bin_checksum;
	unsigned char buf[4];
	const unsigned char *pData;

	for (i = 0; i < 5; i++) {
		buf[0] = 0xA0;
		buf[1] = 0x00;
		ret = cst3xx_i2c_read_register(client, buf, 1);
		if (ret < 0) {
			mdelay(2);
			continue;
		}

		if (buf[0] != 0)
			break;
		else
			mdelay(2);
	}
	mdelay(2);

	if (buf[0] == 0x01) {
		buf[0] = 0xA0;
		buf[1] = 0x08;
		ret = cst3xx_i2c_read_register(client, buf, 4);

		if (ret < 0)
			return -1;

		// read chip checksum
		checksum = buf[0] + (buf[1] << 8) + (buf[2] << 16) +
			   (buf[3] << 24);

		pData = (unsigned char *)
				hyn_ts_data->p_hynitron_upgrade_firmware +
			CST644K_BIN_LEN - 4; // 7*1024 +512
		bin_checksum = pData[0] + (pData[1] << 8) + (pData[2] << 16) +
			       (pData[3] << 24);

		HYN_INFO(" the updated ic checksum is :0x%x. the updating "
			 "firmware "
			 "checksum is:0x%x------\n",
			 checksum, bin_checksum);

		if (checksum != bin_checksum) {
			HYN_ERROR("hyn check sum error.\n");
			return -1;
		}

	} else {
		HYN_INFO("hyn No checksum.\n");
		return -1;
	}
	return 0;
}

static int cst644k_exit_program_mode(struct i2c_client *client) {
	int ret;
	unsigned char buf[3];

	buf[0] = 0xA0;
	buf[1] = 0x06;
	buf[2] = 0xEE;
	ret = cst3xx_i2c_write(client, buf, 3);
	if (ret < 0)
		return -1;

	mdelay(10); // wait for restart

	return 0;
}

static int cst644k_update_firmware(struct i2c_client *client,
				   const unsigned char *pdata) {
	int ret;
	int retry_timer = 0;
	int retry = 0;

	HYN_INFO("----------upgrade cst644k begain------------\n");
	mdelay(20);

	for (retry = 0; retry < 30; retry++) {
		hyn_reset_proc(5 + retry_timer);
		client->addr = hyn_ts_data->chip_ic_bootloader_addr;
		hyn_ts_data->fw_updating = 1;
		ret = cst644k_into_program_mode(client);
		if (ret < 0) {
			HYN_ERROR("into program mode failed.\n");
			if (retry_timer < 20)
				retry_timer++;
			continue;
		}
		hyn_ts_data->fw_updating = 2;
		ret = cst644k_write_program_data(client, pdata);
		if (ret < 0) {
			HYN_ERROR("write program data into cstxxx failed.\n");
			continue;
		}
		hyn_ts_data->fw_updating = 3;
		ret = cst644k_check_checksum(client);
		if (ret < 0) {
			HYN_ERROR("after write program cst6xx_check_checksum "
				  "failed.\n");
			continue;
		}
		hyn_ts_data->fw_updating = 4;
		ret = cst644k_exit_program_mode(client);
		if (ret < 0) {
			HYN_ERROR("exit program mode failed.\n");
			continue;
		}
		client->addr = hyn_ts_data->chip_ic_main_addr;
		hyn_reset_proc(30);

		hyn_ts_data->fw_updating = 5;
		HYN_INFO("----------upgrade cst644k end------------\n");

		return 0;
	}

	client->addr = hyn_ts_data->chip_ic_main_addr;
	return -1;
}

static int cst644k_boot_update_fw(struct i2c_client *client) {
	int ret;

	HYN_FUNC_ENTER();

	ret = cst644k_update_firmware(client,
				      hyn_ts_data->p_hynitron_upgrade_firmware);
	if (ret < 0) {
		HYN_ERROR("update firmware failed, fw_updating:%d .\n",
			  hyn_ts_data->fw_updating);
		return -1;
	}
	hyn_ts_data->fw_updating = 0;

	mdelay(50);

	ret = cst3xx_firmware_info(client);
	if (ret < 0) {
		HYN_ERROR(" after update read version and checksum fail.\n");
		return -1;
	}

	HYN_FUNC_EXIT();
	return 0;
}
#endif

#if (HYN_EN_AUTO_UPDATE_CST3xx || HYN_EN_AUTO_UPDATE_CST1xx ||                 \
     HYN_EN_AUTO_UPDATE_CST9xx)

/*******************************************************
Function:
	read checksum in bootloader mode
Input:
	client: i2c client
	strict: check checksum value
Output:
	success: 0
	fail:	-1
*******************************************************/

#define CST3XX_BIN_SIZE (24 * 1024 + 24)

static int cst3xx_check_checksum(struct i2c_client *client) {
	int ret;
	int i;
	unsigned int checksum;
	unsigned int bin_checksum;
	unsigned char buf[4];
	const unsigned char *pData;

	for (i = 0; i < 5; i++) {
		buf[0] = 0xA0;
		buf[1] = 0x00;
		ret = cst3xx_i2c_read_register(client, buf, 1);
		if (ret < 0) {
			mdelay(2);
			continue;
		}

		if (buf[0] != 0)
			break;
		else
			mdelay(2);
	}
	mdelay(2);

	if (buf[0] == 0x01) {
		buf[0] = 0xA0;
		buf[1] = 0x08;
		ret = cst3xx_i2c_read_register(client, buf, 4);

		if (ret < 0)
			return -1;

		// read chip checksum
		checksum = buf[0] + (buf[1] << 8) + (buf[2] << 16) +
			   (buf[3] << 24);

		pData = (unsigned char *)
				hyn_ts_data->p_hynitron_upgrade_firmware +
			24 * 1024 + 16; // 7*1024 +512
		bin_checksum = pData[0] + (pData[1] << 8) + (pData[2] << 16) +
			       (pData[3] << 24);

		HYN_INFO(" the updated ic checksum is :0x%x. the updating "
			 "firmware "
			 "checksum is:0x%x------\n",
			 checksum, bin_checksum);

		if (checksum != bin_checksum) {
			HYN_ERROR("hyn check sum error.\n");
			return -1;
		}

	} else {
		HYN_INFO("hyn No checksum.\n");
		return -1;
	}
	return 0;
}
static int cst3xx_into_program_mode(struct i2c_client *client) {
	int ret;
	unsigned char buf[4];

	buf[0] = 0xA0;
	buf[1] = 0x01;
	buf[2] = 0xAA; // set cmd to enter program mode
	ret = cst3xx_i2c_write(client, buf, 3);
	if (ret < 0)
		return -1;
	mdelay(2);

	buf[0] = 0xA0;
	buf[1] = 0x02; // check whether into program mode
	ret = cst3xx_i2c_read_register(client, buf, 1);
	if (ret < 0)
		return -1;

	if (hyn_ts_data->config_chip_series == HYN_CHIP_CST3XX) {
		if (buf[0] != 0xAC)
			return -1;
	} else {
		if (buf[0] != 0x55)
			return -1;
	}

	return 0;
}

static int cst3xx_exit_program_mode(struct i2c_client *client) {
	int ret;
	unsigned char buf[3];

	buf[0] = 0xA0;
	buf[1] = 0x06;
	buf[2] = 0xEE;
	ret = cst3xx_i2c_write(client, buf, 3);
	mdelay(10); // wait for restart

	return 0;
}

static int cst3xx_erase_program_area(struct i2c_client *client) {
	int ret;
	unsigned char buf[3];

	buf[0] = 0xA0;
	buf[1] = 0x02;
	buf[2] = 0x00; // set cmd to erase main area
	ret = cst3xx_i2c_write(client, buf, 3);
	if (ret < 0)
		return -1;

	mdelay(5);

	buf[0] = 0xA0;
	buf[1] = 0x03;
	ret = cst3xx_i2c_read_register(client, buf, 1);
	if (ret < 0)
		return -1;

	if (buf[0] != 0x55)
		return -1;

	return 0;
}

static int cst3xx_write_program_data(struct i2c_client *client,
				     const unsigned char *pdata) {
	int i, ret;
	unsigned char *i2c_buf;
	unsigned short eep_addr;
	int total_kbyte;
#if HYN_IIC_TRANSFER_LIMIT
	unsigned char temp_buf[8];
	unsigned short iic_addr;
	int j;

#endif

	i2c_buf = kmalloc(sizeof(unsigned char) * (1024 + 2), GFP_KERNEL);
	if (i2c_buf == NULL)
		return -1;

	// make sure fwbin len is N*1K
	// total_kbyte = len / 1024;
	total_kbyte = 24;
	for (i = 0; i < total_kbyte; i++) {
		i2c_buf[0] = 0xA0;
		i2c_buf[1] = 0x14;
		eep_addr = i << 10; // i * 1024
		i2c_buf[2] = eep_addr;
		i2c_buf[3] = eep_addr >> 8;
		ret = cst3xx_i2c_write(client, i2c_buf, 4);
		if (ret < 0)
			goto error_out;

#if HYN_IIC_TRANSFER_LIMIT
		memcpy(i2c_buf, pdata + eep_addr, 1024);
		for (j = 0; j < 256; j++) {
			iic_addr = (j << 2);
			temp_buf[0] = (iic_addr + 0xA018) >> 8;
			temp_buf[1] = (iic_addr + 0xA018) & 0xFF;
			temp_buf[2] = i2c_buf[iic_addr + 0];
			temp_buf[3] = i2c_buf[iic_addr + 1];
			temp_buf[4] = i2c_buf[iic_addr + 2];
			temp_buf[5] = i2c_buf[iic_addr + 3];
			ret = cst3xx_i2c_write(client, temp_buf, 6);
			if (ret < 0)
				goto error_out;
		}
#else

		i2c_buf[0] = 0xA0;
		i2c_buf[1] = 0x18;
		memcpy(i2c_buf + 2, pdata + eep_addr, 1024);
		ret = cst3xx_i2c_write(client, i2c_buf, 1026);
		if (ret < 0)
			goto error_out;
#endif

		i2c_buf[0] = 0xA0;
		i2c_buf[1] = 0x04;
		i2c_buf[2] = 0xEE;
		ret = cst3xx_i2c_write(client, i2c_buf, 3);
		if (ret < 0)
			goto error_out;

		mdelay(60);

		i2c_buf[0] = 0xA0;
		i2c_buf[1] = 0x05;
		ret = cst3xx_i2c_read_register(client, i2c_buf, 1);
		if (ret < 0)
			goto error_out;

		if (i2c_buf[0] != 0x55)
			goto error_out;
	}

	i2c_buf[0] = 0xA0;
	i2c_buf[1] = 0x03;
	i2c_buf[2] = 0x00;
	ret = cst3xx_i2c_write(client, i2c_buf, 3);
	if (ret < 0)
		goto error_out;

	mdelay(8);

	if (i2c_buf != NULL) {
		kfree(i2c_buf);
		i2c_buf = NULL;
	}

	return 0;

error_out:
	if (i2c_buf != NULL) {
		kfree(i2c_buf);
		i2c_buf = NULL;
	}
	return -1;
}

static int cst3xx_update_firmware(struct i2c_client *client,
				  const unsigned char *pdata) {
	int ret;
	int retry_timer = 0;
	int retry = 0;

	HYN_INFO("----------upgrade cst3xx begain------------\n");
	mdelay(20);

START_FLOW:
	hyn_reset_proc(5 + retry_timer);
	client->addr = hyn_ts_data->chip_ic_bootloader_addr;
	hyn_ts_data->fw_updating = 1;
	ret = cst3xx_into_program_mode(client);
	if (ret < 0) {
		HYN_ERROR("into program mode failed.\n");
		if (retry_timer < 20)
			retry_timer++;
		goto err_out;
	}
	hyn_ts_data->fw_updating = 2;
	ret = cst3xx_erase_program_area(client);
	if (ret < 0) {
		HYN_ERROR("erase main area failed.\n");
		goto err_out;
	}
	hyn_ts_data->fw_updating = 3;
	ret = cst3xx_write_program_data(client, pdata);
	if (ret < 0) {
		HYN_ERROR("write program data into cstxxx failed.\n");
		goto err_out;
	}
	hyn_ts_data->fw_updating = 4;
	ret = cst3xx_check_checksum(client);
	if (ret < 0) {
		HYN_ERROR(
			"after write program cst3xx_check_checksum failed.\n");
		goto err_out;
	}
	hyn_ts_data->fw_updating = 5;
	ret = cst3xx_exit_program_mode(client);
	if (ret < 0) {
		HYN_ERROR("exit program mode failed.\n");
		// goto err_out;
	}
	client->addr = hyn_ts_data->chip_ic_main_addr;
	hyn_reset_proc(30);

	hyn_ts_data->fw_updating = 6;
	HYN_INFO(" hyn----------cst3xx_update_firmware success "
		 "end------------\n");

	return 0;

err_out:
	if (retry < 30) {
		retry++;
		mdelay(30);
		goto START_FLOW;
	} else {
		client->addr = hyn_ts_data->chip_ic_main_addr;
		HYN_INFO(" hyn----------cst3xx_update_firmware failed "
			 "end------------\n");
		return -1;
	}
}

static int cst3xx_update_judge(unsigned char *pdata, int strict) {
	unsigned short ic_type, project_id;
	unsigned int fw_checksum, fw_version;
	const unsigned int *p;
	int i;
	unsigned char *pBuf;

	fw_checksum = 0x55;
	p = (const unsigned int *)pdata;
	for (i = 0; i < (CST3XX_BIN_SIZE - 4); i += 4) {
		fw_checksum += (*p);
		p++;
	}

	if (fw_checksum != (*p)) {
		HYN_ERROR("calculated checksum error:0x%x not equal 0x%x.\n",
			  fw_checksum, *p);
		return -1; // bad fw, so do not update
	}

	pBuf = &pdata[CST3XX_BIN_SIZE - 16];

	project_id = pBuf[1];
	project_id <<= 8;
	project_id |= pBuf[0];

	ic_type = pBuf[3];
	ic_type <<= 8;
	ic_type |= pBuf[2];

	fw_version = pBuf[7];
	fw_version <<= 8;
	fw_version |= pBuf[6];
	fw_version <<= 8;
	fw_version |= pBuf[5];
	fw_version <<= 8;
	fw_version |= pBuf[4];

	fw_checksum = pBuf[11];
	fw_checksum <<= 8;
	fw_checksum |= pBuf[10];
	fw_checksum <<= 8;
	fw_checksum |= pBuf[9];
	fw_checksum <<= 8;
	fw_checksum |= pBuf[8];

	HYN_INFO("the updating firmware:project_id:0x%04x, ic type:0x%04x, "
		 "fw_version:0x%x, checksum:0x%x\n",
		 project_id, ic_type, fw_version, fw_checksum);

#if HYN_UPDATE_FIRMWARE_FORCE
	HYN_INFO("update firmware online force.\n");
	return 0;
#endif

	if (hyn_ts_data->chip_ic_checkcode == 0xffffffff) {
		HYN_ERROR("chip ic have no firmware, start update config "
			  "firmware.\n");
		return 0;
	} else if ((hyn_ts_data->chip_ic_type != ic_type) ||
		   ((hyn_ts_data->chip_ic_project_id != project_id) &&
		    (hyn_ts_data->apk_upgrade_flag == 0))) {

		HYN_ERROR("chip_ic_type:0x%04x, chip_ic_project_id:0x%04x, not "
			  "match.\n",
			  hyn_ts_data->chip_ic_type,
			  hyn_ts_data->chip_ic_project_id);
		return -1;
	}

	if (strict > 0) {
		if (hyn_ts_data->chip_ic_checksum != fw_checksum) {

			if (hyn_ts_data->chip_ic_fw_version > fw_version) {
				HYN_ERROR("fw version(%d), ic version(%d).\n",
					  fw_version,
					  hyn_ts_data->chip_ic_fw_version);
				return -1;
			}
		} else {
			HYN_ERROR("fw checksum(0x%x), ic checksum(0x%x).\n",
				  fw_checksum, hyn_ts_data->chip_ic_checksum);
			return -1;
		}
	}

	return 0;
}

static int cst3xx_boot_update_fw(struct i2c_client *client) {
	int ret;

	HYN_FUNC_ENTER();

	if (hyn_ts_data->apk_upgrade_flag == 0) {
		ret = cst3xx_update_judge(
			hyn_ts_data->p_hynitron_upgrade_firmware, 1);
	} else {
		ret = cst3xx_update_judge(
			hyn_ts_data->p_hynitron_upgrade_firmware, 0);
	}
	if (ret < 0) {
		HYN_ERROR(" update_judge fail, no need to update firmware.\n");
		return 0;
	}

	ret = cst3xx_update_firmware(client,
				     hyn_ts_data->p_hynitron_upgrade_firmware);
	if (ret < 0) {
		HYN_ERROR("update firmware failed, fw_updating:%d .\n",
			  hyn_ts_data->fw_updating);
		return -1;
	}
	hyn_ts_data->fw_updating = 0;

	mdelay(50);

	ret = cst3xx_firmware_info(client);
	if (ret < 0) {
		HYN_ERROR(" after update read version and checksum fail.\n");
		return -1;
	}

	HYN_FUNC_EXIT();
	return 0;
}
#endif

#if (HYN_EN_AUTO_UPDATE_CST3240)

/*******************************************************
Function:
	read checksum in bootloader mode
Input:
	client: i2c client
	strict: check checksum value
Output:
	success: 0
	fail:	-1
*******************************************************/

#define CST3240_BIN_SIZE (31 * 512 + 480)

static int cst3240_check_checksum(struct i2c_client *client) {
	int ret;
	int i;
	unsigned int checksum;
	unsigned int bin_checksum;
	unsigned char buf[4];
	const unsigned char *pData;

	for (i = 0; i < 100; i++) {
		buf[0] = 0xA0;
		buf[1] = 0x00;
		ret = cst3xx_i2c_read_register(client, buf, 1);
		if (ret < 0) {
			mdelay(2);
			continue;
		}

		if (buf[0] != 0)
			break;
		else
			mdelay(2);
	}
	mdelay(2);

	if (buf[0] == 0x01) {
		buf[0] = 0xA0;
		buf[1] = 0x08;
		ret = cst3xx_i2c_read_register(client, buf, 4);

		if (ret < 0)
			return -1;

		// read chip checksum
		checksum = buf[0] + (buf[1] << 8) + (buf[2] << 16) +
			   (buf[3] << 24);

		pData = (unsigned char *)
				hyn_ts_data->p_hynitron_upgrade_firmware +
			31 * 512 + 476; // 7*1024 +512
		bin_checksum = pData[0] + (pData[1] << 8) + (pData[2] << 16) +
			       (pData[3] << 24);

		HYN_INFO(" the updated ic checksum is :0x%x. the updating "
			 "firmware "
			 "checksum is:0x%x------\n",
			 checksum, bin_checksum);

		if (checksum != bin_checksum) {
			HYN_ERROR("hyn check sum error.\n");
			return -1;
		}

	} else {
		HYN_INFO("hyn No checksum.\n");
		return -1;
	}
	return 0;
}
static int cst3240_into_program_mode(struct i2c_client *client) {
	int ret;
	unsigned char buf[4];

	buf[0] = 0xA0;
	buf[1] = 0x01;
	buf[2] = 0xAA; // set cmd to enter program mode
	ret = cst3xx_i2c_write(client, buf, 3);
	if (ret < 0)
		return -1;
	mdelay(2);

	buf[0] = 0xA0;
	buf[1] = 0x02; // check whether into program mode
	ret = cst3xx_i2c_read_register(client, buf, 3);
	if (ret < 0)
		return -1;

	if (buf[0] != 0x55)
		return -1;

	mdelay(2);
	buf[0] = 0xA0;
	buf[1] = 0x03; // check whether into program mode
	ret = cst3xx_i2c_read_register(client, buf, 3);
	if (ret < 0)
		return -1;

	if (buf[0] != 0xA8)
		return -1;

	mdelay(2);
	buf[0] = 0xA0;
	buf[1] = 0x01;
	buf[2] = 0x00;
	ret = cst3xx_i2c_write(client, buf, 3);
	if (ret < 0)
		return -1;

	return 0;
}

static int cst3240_exit_program_mode(struct i2c_client *client) {
	int ret;
	unsigned char buf[3];

	buf[0] = 0xA0;
	buf[1] = 0x06;
	buf[2] = 0xEE;
	ret = cst3xx_i2c_write(client, buf, 3);
	mdelay(10); // wait for restart

	return 0;
}

static int cst3240_write_program_data(struct i2c_client *client,
				      const unsigned char *pdata) {
	int i, ret, k;
	unsigned char *i2c_buf;
	unsigned short eep_addr;
	int total_kbyte;
#if HYN_IIC_TRANSFER_LIMIT
	unsigned char temp_buf[8];
	unsigned short iic_addr;
	int j;

#endif

	i2c_buf = kmalloc(sizeof(unsigned char) * (512 + 2), GFP_KERNEL);
	if (i2c_buf == NULL)
		return -1;

	i2c_buf[0] = 0xA0;
	i2c_buf[1] = 0x10;
	i2c_buf[2] = 0x00;
	ret = cst3xx_i2c_write(client, i2c_buf, 3);
	if (ret < 0)
		return -1;

	// make sure fwbin len is N*1K
	// total_kbyte = len / 1024;
	total_kbyte = 31;
	for (i = 0; i < total_kbyte; i++) {
		i2c_buf[0] = 0xA0;
		i2c_buf[1] = 0x14;
		eep_addr = i << 9; // i * 1024
		i2c_buf[2] = eep_addr;
		i2c_buf[3] = eep_addr >> 8;
		ret = cst3xx_i2c_write(client, i2c_buf, 4);
		if (ret < 0)
			goto error_out;

#if HYN_IIC_TRANSFER_LIMIT
		memcpy(i2c_buf, pdata + eep_addr, 512);
		for (j = 0; j < 128; j++) {
			iic_addr = (j << 2);
			temp_buf[0] = (iic_addr + 0xA018) >> 8;
			temp_buf[1] = (iic_addr + 0xA018) & 0xFF;
			temp_buf[2] = i2c_buf[iic_addr + 0];
			temp_buf[3] = i2c_buf[iic_addr + 1];
			temp_buf[4] = i2c_buf[iic_addr + 2];
			temp_buf[5] = i2c_buf[iic_addr + 3];
			ret = cst3xx_i2c_write(client, temp_buf, 6);
			if (ret < 0)
				goto error_out;
		}
#else

		i2c_buf[0] = 0xA0;
		i2c_buf[1] = 0x18;
		memcpy(i2c_buf + 2, pdata + eep_addr, 512);
		ret = cst3xx_i2c_write(client, i2c_buf, 514);
		if (ret < 0)
			goto error_out;
#endif

		i2c_buf[0] = 0xA0;
		i2c_buf[1] = 0x04;
		i2c_buf[2] = 0xEE;
		ret = cst3xx_i2c_write(client, i2c_buf, 3);
		if (ret < 0)
			goto error_out;

		mdelay(5);
		for (k = 0; k < 200; k++) {

			i2c_buf[0] = 0xA0;
			i2c_buf[1] = 0x05;
			ret = cst3xx_i2c_read_register(client, i2c_buf, 1);
			if (ret < 0)
				goto error_out;

			if (i2c_buf[0] == 0x55)
				break;

			mdelay(10);
		}
		if (k == 200)
			goto error_out;
	}

	i2c_buf[0] = 0xA0;
	i2c_buf[1] = 0x10;
	i2c_buf[2] = 0x02;
	ret = cst3xx_i2c_write(client, i2c_buf, 3);
	if (ret < 0)
		return -1;

	i2c_buf[0] = 0xA0;
	i2c_buf[1] = 0x0C;
	i2c_buf[2] = 0x1E0 & 0xff; // 480
	i2c_buf[3] = 0x1E0 >> 8;
	ret = cst3xx_i2c_write(client, i2c_buf, 4);
	if (ret < 0)
		return -1;

	i2c_buf[0] = 0xA0;
	i2c_buf[1] = 0x14;
	eep_addr = 31 << 9; // i * 1024
	i2c_buf[2] = eep_addr;
	i2c_buf[3] = eep_addr >> 8;
	ret = cst3xx_i2c_write(client, i2c_buf, 4);
	if (ret < 0)
		goto error_out;

#if HYN_IIC_TRANSFER_LIMIT
	memcpy(i2c_buf, pdata + eep_addr, 480);
	for (j = 0; j < 120; j++) {
		iic_addr = (j << 2);
		temp_buf[0] = (iic_addr + 0xA018) >> 8;
		temp_buf[1] = (iic_addr + 0xA018) & 0xFF;
		temp_buf[2] = i2c_buf[iic_addr + 0];
		temp_buf[3] = i2c_buf[iic_addr + 1];
		temp_buf[4] = i2c_buf[iic_addr + 2];
		temp_buf[5] = i2c_buf[iic_addr + 3];
		ret = cst3xx_i2c_write(client, temp_buf, 6);
		if (ret < 0)
			goto error_out;
	}
#else

	i2c_buf[0] = 0xA0;
	i2c_buf[1] = 0x18;
	memcpy(i2c_buf + 2, pdata + eep_addr, 480);
	ret = cst3xx_i2c_write(client, i2c_buf, 482);
	if (ret < 0)
		goto error_out;
#endif

	i2c_buf[0] = 0xA0;
	i2c_buf[1] = 0x04;
	i2c_buf[2] = 0xEE;
	ret = cst3xx_i2c_write(client, i2c_buf, 3);
	if (ret < 0)
		goto error_out;

	mdelay(5);

	for (k = 0; k < 200; k++) {

		i2c_buf[0] = 0xA0;
		i2c_buf[1] = 0x05;
		ret = cst3xx_i2c_read_register(client, i2c_buf, 1);
		if (ret < 0)
			goto error_out;

		if (i2c_buf[0] == 0x55)
			break;

		mdelay(10);
	}
	if (k == 200)
		goto error_out;

	i2c_buf[0] = 0xA0;
	i2c_buf[1] = 0x03;
	i2c_buf[2] = 0x00;
	ret = cst3xx_i2c_write(client, i2c_buf, 3);
	if (ret < 0)
		goto error_out;

	mdelay(8);

	if (i2c_buf != NULL) {
		kfree(i2c_buf);
		i2c_buf = NULL;
	}

	return 0;

error_out:
	if (i2c_buf != NULL) {
		kfree(i2c_buf);
		i2c_buf = NULL;
	}
	return -1;
}

static int cst3240_update_firmware(struct i2c_client *client,
				   const unsigned char *pdata) {
	int ret;
	int retry_timer = 0;
	int retry = 0;

	HYN_INFO("----------upgrade cst3xx begain------------\n");
	mdelay(20);

START_FLOW:
	hyn_reset_proc(5 + retry_timer);
	client->addr = hyn_ts_data->chip_ic_bootloader_addr;

	ret = cst3240_into_program_mode(client);
	if (ret < 0) {
		HYN_ERROR("into program mode failed.\n");
		if (retry_timer < 20)
			retry_timer++;
		goto err_out;
	}
	ret = cst3240_write_program_data(client, pdata);
	if (ret < 0) {
		HYN_ERROR("write program data into cstxxx failed.\n");
		goto err_out;
	}
	ret = cst3240_check_checksum(client);
	if (ret < 0) {
		HYN_ERROR(
			"after write program cst3xx_check_checksum failed.\n");
		goto err_out;
	}
	ret = cst3240_exit_program_mode(client);
	if (ret < 0) {
		HYN_ERROR("exit program mode failed.\n");
		// goto err_out;
	}
	client->addr = hyn_ts_data->chip_ic_main_addr;
	hyn_reset_proc(30);

	HYN_INFO(" hyn----------cst3xx_update_firmware  end------------\n");

	return 0;

err_out:
	if (retry < 30) {
		retry++;
		mdelay(30);
		goto START_FLOW;
	} else {
		client->addr = hyn_ts_data->chip_ic_main_addr;
		return -1;
	}
}

static int cst3240_update_judge(unsigned char *pdata, int strict) {
	unsigned short ic_type, project_id;
	unsigned int fw_checksum, fw_version;
	const unsigned int *p;
	int i;
	unsigned char *pBuf;

	fw_checksum = 0x55;
	p = (const unsigned int *)pdata;
	for (i = 0; i < (CST3240_BIN_SIZE - 4); i += 4) {
		fw_checksum += (*p);
		p++;
	}

	if (fw_checksum != (*p)) {
		HYN_ERROR("calculated checksum error:0x%x not equal 0x%x.\n",
			  fw_checksum, *p);
		return -1; // bad fw, so do not update
	}

	pBuf = &pdata[CST3240_BIN_SIZE - 16 + 4];

	project_id = pBuf[1];
	project_id <<= 8;
	project_id |= pBuf[0];

	ic_type = pBuf[3];
	ic_type <<= 8;
	ic_type |= pBuf[2];

	fw_version = pBuf[7];
	fw_version <<= 8;
	fw_version |= pBuf[6];
	fw_version <<= 8;
	fw_version |= pBuf[5];
	fw_version <<= 8;
	fw_version |= pBuf[4];

	fw_checksum = pBuf[11];
	fw_checksum <<= 8;
	fw_checksum |= pBuf[10];
	fw_checksum <<= 8;
	fw_checksum |= pBuf[9];
	fw_checksum <<= 8;
	fw_checksum |= pBuf[8];

	HYN_INFO("the updating firmware:project_id:0x%04x, ic type:0x%04x, "
		 "fw_version:0x%x, checksum:0x%x\n",
		 project_id, ic_type, fw_version, fw_checksum);

#if HYN_UPDATE_FIRMWARE_FORCE
	HYN_INFO("update firmware online force.\n");
	return 0;
#endif

	if (hyn_ts_data->chip_ic_checkcode == 0xffffffff) {
		HYN_ERROR("chip ic have no firmware, start update config "
			  "firmware.\n");
		return 0;
	} else if ((hyn_ts_data->chip_ic_type != ic_type) ||
		   ((hyn_ts_data->chip_ic_project_id != project_id) &&
		    (hyn_ts_data->apk_upgrade_flag == 0))) {

		HYN_ERROR("chip_ic_type:0x%04x, chip_ic_project_id:0x%04x, not "
			  "match.\n",
			  hyn_ts_data->chip_ic_type,
			  hyn_ts_data->chip_ic_project_id);
		return -1;
	}

	if (strict > 0) {
		if (hyn_ts_data->chip_ic_checksum != fw_checksum) {

			if (hyn_ts_data->chip_ic_fw_version > fw_version) {
				HYN_ERROR("fw version(%d), ic version(%d).\n",
					  fw_version,
					  hyn_ts_data->chip_ic_fw_version);
				return -1;
			}
		} else {
			HYN_ERROR("fw checksum(0x%x), ic checksum(0x%x).\n",
				  fw_checksum, hyn_ts_data->chip_ic_checksum);
			return -1;
		}
	}

	return 0;
}

static int cst3240_boot_update_fw(struct i2c_client *client) {
	int ret = -1;

	HYN_FUNC_ENTER();

	if (hyn_ts_data->apk_upgrade_flag == 0) {
		ret = cst3240_update_judge(
			hyn_ts_data->p_hynitron_upgrade_firmware, 1);
		if (ret < 0) {
			HYN_ERROR(" update_judge fail, no need to update "
				  "firmware.\n");
			return 0;
		}
	} else {
		ret = cst3240_update_judge(
			hyn_ts_data->p_hynitron_upgrade_firmware, 0);
		if (ret < 0) {
			HYN_ERROR(" update_judge fail, no need to update "
				  "firmware.\n");
			return 0;
		}
	}

	ret = cst3240_update_firmware(client,
				      hyn_ts_data->p_hynitron_upgrade_firmware);
	if (ret < 0) {
		HYN_ERROR("update firmware failed, fw_updating:%d .\n",
			  hyn_ts_data->fw_updating);
		return -1;
	}
	hyn_ts_data->fw_updating = 0;

	mdelay(50);

	ret = cst3xx_firmware_info(client);
	if (ret < 0) {
		HYN_ERROR(" after update read version and checksum fail.\n");
		return -1;
	}

	HYN_FUNC_EXIT();
	return 0;
}
#endif

#if (HYN_EN_AUTO_UPDATE_CST92XX)

/*******************************************************
Function:
	read checksum in bootloader mode
Input:
	client: i2c client
	strict: check checksum value
Output:
	success: 0
	fail:	-1
*******************************************************/

#define CST92XX_BIN_SIZE (32 * 1024 - 128)

static int cst92xx_check_checksum(struct i2c_client *client) {
	int ret;
	int i;
	unsigned int checksum;
	unsigned int bin_checksum;
	unsigned char buf[4];
	const unsigned char *pData;

	// enter checksum check
	buf[0] = 0xA0;
	buf[1] = 0x03;
	buf[2] = 0x00;
	ret = cst3xx_i2c_write(client, buf, 3);
	if (ret < 0)
		return -1;

	mdelay(10);
	for (i = 0; i < 40; i++) {
		buf[0] = 0xA0;
		buf[1] = 0x00;
		ret = cst3xx_i2c_read_register(client, buf, 1);
		if (ret < 0) {
			mdelay(2);
			continue;
		}

		if (buf[0] == 0x01)
			break;
		else
			mdelay(1);
	}
	mdelay(2);
	if (buf[0] == 0x01) {
		buf[0] = 0xA0;
		buf[1] = 0x08;
		ret = cst3xx_i2c_read_register(client, buf, 4);

		if (ret < 0)
			return -1;

		// read chip checksum
		checksum = buf[0] + (buf[1] << 8) + (buf[2] << 16) +
			   (buf[3] << 24);

		pData = (unsigned char *)
				hyn_ts_data->p_hynitron_upgrade_firmware +
			CST92XX_BIN_SIZE - 16 - 4;
		bin_checksum = pData[0] + (pData[1] << 8) + (pData[2] << 16) +
			       (pData[3] << 24);

		HYN_INFO(" the updated ic checksum is :0x%x. the updating "
			 "firmware "
			 "checksum is:0x%x------\n",
			 checksum, bin_checksum);

		if (checksum != bin_checksum) {
			HYN_ERROR("hyn check sum error.\n");
			return -1;
		}

	} else {
		HYN_INFO("hyn No checksum.\n");
		return -1;
	}
	return 0;
}
static int cst92xx_into_program_mode(struct i2c_client *client) {
	int ret;
	unsigned char buf[4];
	unsigned char check_cnt = 0;

	for (check_cnt = 0; check_cnt < 5; check_cnt++) {
		buf[0] = 0xA0;
		buf[1] = 0x01;
		buf[2] = 0xAA; // set cmd to enter program mode
		ret = cst3xx_i2c_write(client, buf, 3);
		if (ret < 0)
			return -1;
		mdelay(2);

		buf[0] = 0xA0;
		buf[1] = 0x02; // check whether into program mode
		ret = cst3xx_i2c_read_register(client, buf, 2);
		if (ret < 0)
			return -1;

		if ((buf[0] == 0x55) && (buf[1] == 0xB0)) {
			break;
		}
	}

	HYN_DEBUG("cst92xx_into_program_mode:%x, %x", buf[0], buf[1]);

	// mdelay(2);
	// buf[0] = 0xA0;
	// buf[1] = 0x03;	//check whether into program mode
	// ret = cst3xx_i2c_read_register(client, buf, 1);
	// if (ret < 0)  return -1;

	// if (buf[0] != 0xB0)
	//	return -1;

	mdelay(2);
	buf[0] = 0xA0;
	buf[1] = 0x01;
	buf[2] = 0x00;
	ret = cst3xx_i2c_write(client, buf, 3);
	if (ret < 0)
		return -1;

	return 0;
}

static int cst92xx_exit_program_mode(struct i2c_client *client) {
	int ret;
	unsigned char buf[3];

	buf[0] = 0xA0;
	buf[1] = 0x06;
	buf[2] = 0xEE;
	ret = cst3xx_i2c_write(client, buf, 3);
	mdelay(10); // wait for restart

	return 0;
}
static int cst92xx_erase_flash_data(struct i2c_client *client) {
	int ret = -1;
	unsigned char i2c_buf[8];
	unsigned char retry = 0;

	// erse flash
	i2c_buf[0] = 0xA0;
	i2c_buf[1] = 0x14;
	i2c_buf[2] = 0x00;
	i2c_buf[3] = 0x00;
	ret = cst3xx_i2c_write(client, i2c_buf, 4);
	if (ret < 0)
		return -1;

	i2c_buf[0] = 0xA0;
	i2c_buf[1] = 0x0C;
	i2c_buf[2] = 0x80;
	i2c_buf[3] = 0x7F;
	ret = cst3xx_i2c_write(client, i2c_buf, 4);
	if (ret < 0)
		return -1;

	i2c_buf[0] = 0xA0;
	i2c_buf[1] = 0x04;
	i2c_buf[2] = 0xEC;
	ret = cst3xx_i2c_write(client, i2c_buf, 3);
	if (ret < 0)
		return -1;

	for (retry = 0; retry < 100; retry++) {

		mdelay(10);
		// timeout
		i2c_buf[0] = 0xA0;
		i2c_buf[1] = 0x05;
		ret = cst3xx_i2c_read_register(client, i2c_buf, 1);
		if (ret < 0)
			return -1;

		if (i2c_buf[0] == 0x88) {
			break;
		}
		mdelay(10);
	}
	if (retry == 100)
		return -1;
	else
		return 0;
}
static int cst92xx_write_program_data(struct i2c_client *client,
				      const unsigned char *pdata) {
	int i, ret, k;
	unsigned char *i2c_buf;
	unsigned short eep_addr;
	unsigned int total_kbyte;
	unsigned int single_kbyte;
#if HYN_IIC_TRANSFER_LIMIT
	unsigned char temp_buf[8];
	unsigned short iic_addr;
	int j;

#endif

	i2c_buf = kmalloc(sizeof(unsigned char) * (1024 + 2), GFP_KERNEL);
	if (i2c_buf == NULL)
		return -1;

	//	i2c_buf[0] = 0xA0;
	//	i2c_buf[1] = 0x10;
	//	i2c_buf[2] = 0x00;
	//	ret = cst3xx_i2c_write(client, i2c_buf, 3);
	//	if (ret < 0)  return -1;

	// total_kbyte = 32 *1024;-128
	total_kbyte = 32;
	for (i = 0; i < total_kbyte; i++) {

		if (i == 31) {
			// length =0x0380 =896
			i2c_buf[0] = 0xA0;
			i2c_buf[1] = 0x0C;
			i2c_buf[2] = 0x80;
			i2c_buf[3] = 0x03;

			single_kbyte = 896;

		} else {
			// length =0x0400 =1024
			i2c_buf[0] = 0xA0;
			i2c_buf[1] = 0x0C;
			i2c_buf[2] = 0x00;
			i2c_buf[3] = 0x04;

			single_kbyte = 1024;
		}
		ret = cst3xx_i2c_write(client, i2c_buf, 4);
		if (ret < 0)
			goto error_out;

		i2c_buf[0] = 0xA0;
		i2c_buf[1] = 0x14;
		eep_addr = i << 10; // i * 1024
		i2c_buf[2] = eep_addr;
		i2c_buf[3] = eep_addr >> 8;
		ret = cst3xx_i2c_write(client, i2c_buf, 4);
		if (ret < 0)
			goto error_out;

#if HYN_IIC_TRANSFER_LIMIT

		memcpy(i2c_buf, pdata + eep_addr, single_kbyte);
		for (j = 0; j < (single_kbyte >> 2); j++) {
			iic_addr = (j << 2);
			temp_buf[0] = (iic_addr + 0xA018) >> 8;
			temp_buf[1] = (iic_addr + 0xA018) & 0xFF;
			temp_buf[2] = i2c_buf[iic_addr + 0];
			temp_buf[3] = i2c_buf[iic_addr + 1];
			temp_buf[4] = i2c_buf[iic_addr + 2];
			temp_buf[5] = i2c_buf[iic_addr + 3];
			ret = cst3xx_i2c_write(client, temp_buf, 6);
			if (ret < 0)
				goto error_out;
		}
#else

		i2c_buf[0] = 0xA0;
		i2c_buf[1] = 0x18;
		memcpy(i2c_buf + 2, pdata + eep_addr, single_kbyte);
		ret = cst3xx_i2c_write(client, i2c_buf, (2 + single_kbyte));
		if (ret < 0)
			goto error_out;
#endif

		i2c_buf[0] = 0xA0;
		i2c_buf[1] = 0x04;
		i2c_buf[2] = 0xEE;
		ret = cst3xx_i2c_write(client, i2c_buf, 3);
		if (ret < 0)
			goto error_out;

		mdelay(40);

		for (k = 0; k < 50; k++) {

			i2c_buf[0] = 0xA0;
			i2c_buf[1] = 0x05;
			ret = cst3xx_i2c_read_register(client, i2c_buf, 1);
			if (ret < 0)
				goto error_out;

			if (i2c_buf[0] == 0x55)
				break;

			mdelay(1);
		}
		if (k == 50)
			goto error_out;
	}

	if (i2c_buf != NULL) {
		kfree(i2c_buf);
		i2c_buf = NULL;
	}

	return 0;

error_out:
	if (i2c_buf != NULL) {
		kfree(i2c_buf);
		i2c_buf = NULL;
	}
	return -1;
}

static int cst92xx_update_firmware(struct i2c_client *client,
				   const unsigned char *pdata) {
	int ret;
	int retry_timer = 0;
	int retry = 0;

	HYN_INFO("----------upgrade cst92xx begain------------\n");
	mdelay(20);

START_FLOW:
	hyn_reset_proc(5 + retry_timer);
	client->addr = hyn_ts_data->chip_ic_bootloader_addr;

	ret = cst92xx_into_program_mode(client);
	if (ret < 0) {
		HYN_ERROR("into program mode failed.\n");
		if (retry_timer < 20)
			retry_timer++;
		goto err_out;
	}

	ret = cst92xx_erase_flash_data(client);
	if (ret < 0) {
		HYN_ERROR("erase flash data failed.\n");
		goto err_out;
	}
	ret = cst92xx_write_program_data(client, pdata);
	if (ret < 0) {
		HYN_ERROR("write program data into cstxxx failed.\n");
		goto err_out;
	}
	ret = cst92xx_check_checksum(client);
	if (ret < 0) {
		HYN_ERROR(
			"after write program cst3xx_check_checksum failed.\n");
		goto err_out;
	}
	ret = cst92xx_exit_program_mode(client);
	if (ret < 0) {
		HYN_ERROR("exit program mode failed.\n");
		// goto err_out;
	}
	client->addr = hyn_ts_data->chip_ic_main_addr;
	hyn_reset_proc(30);

	HYN_INFO(" hyn----------cst3xx_update_firmware  end------------\n");

	return 0;

err_out:
	if (retry < 30) {
		retry++;
		mdelay(30);
		goto START_FLOW;
	} else {
		client->addr = hyn_ts_data->chip_ic_main_addr;
		return -1;
	}
}

static int cst92xx_update_judge(unsigned char *pdata, int strict) {
	unsigned short ic_type, project_id;
	unsigned int fw_checksum, fw_version;
	const unsigned int *p;
	int i;
	unsigned char *pBuf;

	fw_checksum = 0x55;
	p = (const unsigned int *)pdata;
	for (i = 0; i < (CST92XX_BIN_SIZE - 20); i += 4) {
		fw_checksum += (*p);
		p++;
	}

	if (fw_checksum != (*p)) {
		HYN_ERROR("calculated checksum error:0x%x not equal 0x%x.\n",
			  fw_checksum, *p);
		return -1; // bad fw, so do not update
	}

	pBuf = &pdata[CST92XX_BIN_SIZE - 16 - 12];

	project_id = pBuf[1];
	project_id <<= 8;
	project_id |= pBuf[0];

	ic_type = pBuf[3];
	ic_type <<= 8;
	ic_type |= pBuf[2];

	fw_version = pBuf[7];
	fw_version <<= 8;
	fw_version |= pBuf[6];
	fw_version <<= 8;
	fw_version |= pBuf[5];
	fw_version <<= 8;
	fw_version |= pBuf[4];

	fw_checksum = pBuf[11];
	fw_checksum <<= 8;
	fw_checksum |= pBuf[10];
	fw_checksum <<= 8;
	fw_checksum |= pBuf[9];
	fw_checksum <<= 8;
	fw_checksum |= pBuf[8];

	HYN_INFO("the updating firmware:project_id:0x%04x, ic type:0x%04x, "
		 "fw_version:0x%x, checksum:0x%x\n",
		 project_id, ic_type, fw_version, fw_checksum);

#if HYN_UPDATE_FIRMWARE_FORCE
	HYN_INFO("update firmware online force.\n");
	return 0;
#endif

	if (hyn_ts_data->chip_ic_checkcode == 0xffffffff) {
		HYN_ERROR("chip ic have no firmware, start update config "
			  "firmware.\n");
		return 0;
	} else if ((hyn_ts_data->chip_ic_type != ic_type) ||
		   ((hyn_ts_data->chip_ic_project_id != project_id) &&
		    (hyn_ts_data->apk_upgrade_flag == 0))) {

		HYN_ERROR("chip_ic_type:0x%04x, chip_ic_project_id:0x%04x, not "
			  "match.\n",
			  hyn_ts_data->chip_ic_type,
			  hyn_ts_data->chip_ic_project_id);
		return -1;
	}

	if (strict > 0) {
		if (hyn_ts_data->chip_ic_checksum != fw_checksum) {

			if (hyn_ts_data->chip_ic_fw_version > fw_version) {
				HYN_ERROR("fw version(%d), ic version(%d).\n",
					  fw_version,
					  hyn_ts_data->chip_ic_fw_version);
				return -1;
			}
		} else {
			HYN_ERROR("fw checksum(0x%x), ic checksum(0x%x).\n",
				  fw_checksum, hyn_ts_data->chip_ic_checksum);
			return -1;
		}
	}

	return 0;
}

static int cst92xx_boot_update_fw(struct i2c_client *client) {
	int ret;

	HYN_FUNC_ENTER();

	if (hyn_ts_data->apk_upgrade_flag == 0)
		ret = cst92xx_update_judge(
			hyn_ts_data->p_hynitron_upgrade_firmware, 1);
	else
		ret = cst92xx_update_judge(
			hyn_ts_data->p_hynitron_upgrade_firmware, 0);
	if (ret < 0) {
		HYN_ERROR(" update_judge fail, no need to update firmware.\n");
		return 0;
	}

	ret = cst92xx_update_firmware(client,
				      hyn_ts_data->p_hynitron_upgrade_firmware);
	if (ret < 0) {
		HYN_ERROR("update firmware failed, fw_updating:%d .\n",
			  hyn_ts_data->fw_updating);
		return -1;
	}
	hyn_ts_data->fw_updating = 0;

	mdelay(50);

	ret = cst3xx_firmware_info(client);
	if (ret < 0) {
		HYN_ERROR(" after update read version and checksum fail.\n");
		return -1;
	}

	HYN_FUNC_EXIT();
	return 0;
}
#endif

#endif

#if HYN_SYS_AUTO_SEARCH_FIRMWARE
// system irrelevant
#define ERROR_HN_VER _ERROR(10) // HotKnot version error.
#define ERROR_CHECK _ERROR(11) // Compare src and dst error.
#define ERROR_RETRY _ERROR(12) // Too many retries.
#define ERROR_PATH _ERROR(13) // Mount path error
#define ERROR_FW _ERROR(14)
#define ERROR_FILE _ERROR(15)
#define ERROR_VALUE _ERROR(16) // Illegal value of variables

s32 hyn_check_fs_mounted(char *path_name) {
	struct path root_path;
	struct path path;
	s32 err;

	err = kern_path("/", LOOKUP_FOLLOW, &root_path);
	if (err)
		return -1;

	err = kern_path(path_name, LOOKUP_FOLLOW, &path);
	if (err) {
		err = -1;
		goto check_fs_fail;
	}

	if (path.mnt->mnt_sb == root_path.mnt->mnt_sb) {
		// not mounted
		err = -1;
	} else {
		err = 0;
	}

	path_put(&path);
check_fs_fail:
	path_put(&root_path);
	return err;
}

int hyn_auto_read_file(char *file_name, u8 **file_buf) {
	int ret = 0;
	char file_path[FILE_NAME_LENGTH] = {0};
	struct file *filp = NULL;
	struct inode *inode;
	mm_segment_t old_fs;
	loff_t pos;
	loff_t file_len = 0;
	int retry = 20;

	HYN_FUNC_ENTER();

	if ((file_name == NULL) || (file_buf == NULL)) {
		HYN_ERROR("filename/filebuf is NULL");
		return -EINVAL;
	}

	old_fs = get_fs();
	set_fs(KERNEL_DS);

	snprintf(file_path, FILE_NAME_LENGTH, "%s%s",
		 HYN_UPDATE_FIRMWARE_PATH_1, file_name);
	HYN_INFO("file_path:%s.\n", file_path);

	while (retry-- > 0) {
		msleep(500);
		// check if rootfs is ready
		if (hyn_check_fs_mounted("/data")) {
			HYN_INFO("filesystem is not ready");
			continue;
		}
	}

	filp = filp_open(file_path, O_RDONLY, 0);
	if (IS_ERR(filp)) {
		HYN_ERROR("open %s file fail, try open /sdcard/firmware/",
			  file_path);
		snprintf(file_path, FILE_NAME_LENGTH, "%s%s",
			 HYN_UPDATE_FIRMWARE_PATH_2, file_name);
		HYN_INFO("file_path:%s.\n", file_path);
		filp = filp_open(file_path, O_RDONLY, 0);
		if (IS_ERR(filp)) {
			HYN_ERROR("open %s file fail", file_path);
			goto ERROR;
		}
	}
	inode = filp->f_inode;

	file_len = inode->i_size;
	*file_buf = vmalloc(file_len);
	if (*file_buf == NULL) {
		HYN_ERROR("file buf malloc fail");
		filp_close(filp, NULL);
		goto ERROR;
	}

	pos = 0;
	ret = vfs_read(filp, *file_buf, file_len, &pos);
	if (ret < 0)
		HYN_ERROR("read file fail");
	HYN_INFO("file len:%d read len:%d pos:%d", (u32)file_len, ret,
		 (u32)pos);
	filp_close(filp, NULL);
	set_fs(old_fs);

	HYN_FUNC_EXIT();

	return ret;

ERROR:
	filp_close(filp, NULL);
	set_fs(old_fs);
	return -1;
}

int hyn_sys_auto_search_firmware(void) {
	u8 *fw_file_buf = NULL;
	int ret;
	char *fw_name = NULL;
	char fwname[FILE_NAME_LENGTH] = {0};

	HYN_FUNC_ENTER();

	memset(fwname, 0, sizeof(fwname));
	snprintf(fwname, PAGE_SIZE, "%s", HYN_UPDATE_FIRMWARE_FILTNAME_1);
	fwname[sizeof(HYN_UPDATE_FIRMWARE_FILTNAME_1) - 1] = '\0';
	HYN_INFO("fwname:%s.\n", fwname);
	ret = hyn_auto_read_file(fw_name, &fw_file_buf);
	if (ret < 0) {
		HYN_ERROR("hyn_auto_read_file.\n");
		return -1;
	}
	if (fw_file_buf != NULL) {
		hyn_ts_data->apk_upgrade_flag = 1;
		hyn_ts_data->p_hynitron_upgrade_firmware = fw_file_buf;
	} else {
		HYN_ERROR(
			"hyn_sys_auto_search_firmware fw_file_buf is null.\n");
	}

	HYN_FUNC_EXIT();

	return 0;
}
#endif

/*******************************************************
Function:
	get firmware version, ic type...
Input:
	client: i2c client
Output:
	success: 0
	fail:	-1
*******************************************************/
int cst3xx_firmware_info(struct i2c_client *client) {
	int ret;
	unsigned char buf[28];
	HYN_FUNC_ENTER();

	buf[0] = 0xD1;
	buf[1] = 0x01;
	ret = cst3xx_i2c_write(client, buf, 2);
	if (ret < 0)
		return -1;

	mdelay(10);

	buf[0] = 0xD1;
	buf[1] = 0xFC;
	ret = cst3xx_i2c_read_register(client, buf, 4);
	if (ret < 0)
		return -1;

	// 0xCACA0000
	hyn_ts_data->chip_ic_checkcode = buf[3];
	hyn_ts_data->chip_ic_checkcode <<= 8;
	hyn_ts_data->chip_ic_checkcode |= buf[2];
	hyn_ts_data->chip_ic_checkcode <<= 8;
	hyn_ts_data->chip_ic_checkcode |= buf[1];
	hyn_ts_data->chip_ic_checkcode <<= 8;
	hyn_ts_data->chip_ic_checkcode |= buf[0];

	HYN_INFO("the chip chip_ic_checkcode:0x%x.\r\n",
		 hyn_ts_data->chip_ic_checkcode);
	if ((hyn_ts_data->chip_ic_checkcode & 0xffff0000) != 0xCACA0000) {
		HYN_ERROR("cst3xx_firmware_info read error .\r\n");
		hyn_ts_data->chip_ic_checkcode = 0xffffffff;
		return -1;
	}

	mdelay(10);

	buf[0] = 0xD2;
	buf[1] = 0x04;
	ret = cst3xx_i2c_read_register(client, buf, 4);
	if (ret < 0)
		return -1;
	hyn_ts_data->chip_ic_type = buf[3];
	hyn_ts_data->chip_ic_type <<= 8;
	hyn_ts_data->chip_ic_type |= buf[2];

	hyn_ts_data->config_chip_type = hyn_ts_data->chip_ic_type;

	hyn_ts_data->chip_ic_project_id = buf[1];
	hyn_ts_data->chip_ic_project_id <<= 8;
	hyn_ts_data->chip_ic_project_id |= buf[0];

	HYN_INFO("chip_ic_type :0x%x, chip_ic_project_id:0x%x\r\n",
		 hyn_ts_data->chip_ic_type, hyn_ts_data->chip_ic_project_id);

	mdelay(2);

	buf[0] = 0xD2;
	buf[1] = 0x08;
	ret = cst3xx_i2c_read_register(client, buf, 8);
	if (ret < 0)
		return -1;

	hyn_ts_data->chip_ic_fw_version = buf[3];
	hyn_ts_data->chip_ic_fw_version <<= 8;
	hyn_ts_data->chip_ic_fw_version |= buf[2];
	hyn_ts_data->chip_ic_fw_version <<= 8;
	hyn_ts_data->chip_ic_fw_version |= buf[1];
	hyn_ts_data->chip_ic_fw_version <<= 8;
	hyn_ts_data->chip_ic_fw_version |= buf[0];

	hyn_ts_data->chip_ic_checksum = buf[7];
	hyn_ts_data->chip_ic_checksum <<= 8;
	hyn_ts_data->chip_ic_checksum |= buf[6];
	hyn_ts_data->chip_ic_checksum <<= 8;
	hyn_ts_data->chip_ic_checksum |= buf[5];
	hyn_ts_data->chip_ic_checksum <<= 8;
	hyn_ts_data->chip_ic_checksum |= buf[4];

	HYN_INFO("chip_ic_fw_version:0x%x, chip_ic_checksum:0x%x\r\n",
		 hyn_ts_data->chip_ic_fw_version,
		 hyn_ts_data->chip_ic_checksum);

	if (hyn_ts_data->chip_ic_fw_version == 0xA5A5A5A5) {
		HYN_ERROR(" the chip ic don't have firmware. \n");
		return -1;
	}

	buf[0] = 0xD0;
	buf[1] = 0x47;
	ret = cst3xx_i2c_read_register(client, buf, 1);
	if (ret < 0)
		return -1;

	hyn_ts_data->chip_ic_module_id = buf[0];
	HYN_INFO("chip_ic_module_id:0x%x.\n", hyn_ts_data->chip_ic_module_id);

	buf[0] = 0xD1;
	buf[1] = 0x09;
	ret = cst3xx_i2c_write(client, buf, 2);
	if (ret < 0)
		return -1;
	mdelay(5);

	HYN_FUNC_EXIT();
	return 0;
}

/*
 *
 */

int cst8xx_firmware_info(struct i2c_client *mclient) {
	int ret = -1;
	unsigned char reg_buf[2];
	unsigned char buf[4];
	unsigned short fwversion, chipversion;

	HYN_FUNC_ENTER();
	msleep(100);

	reg_buf[0] = 0xA6;

	ret = hyn_i2c_read(hyn_ts_data->client, reg_buf, 1, buf, 2);

	if (ret < 0) {
		dev_err(&hyn_ts_data->client->dev, "f%s: i2c read error.\n",
			__func__);
		return -1;
	}

	fwversion = *(hyn_ts_data->p_hynitron_upgrade_firmware + 0x3BFD + 6);
	fwversion <<= 8;
	fwversion += *(hyn_ts_data->p_hynitron_upgrade_firmware + 0x3BFC + 6);
	HYN_INFO("\r\nhyn fwversion: %x\r\n", fwversion);

	chipversion = buf[1];
	chipversion <<= 8;
	chipversion += buf[0];
	hyn_ts_data->chip_ic_fw_version = chipversion;

	HYN_INFO("\r\nhyn chipversion: %x\r\n", chipversion);

	if (chipversion > fwversion) {
		update_fw_flag = 0;

		HYN_INFO("\r\nhyn update_fw_flag: %x\r\n", update_fw_flag);
	}
	HYN_FUNC_EXIT();
	return ret;
}

static int hyn_find_fw_idx(u8 check_project_id) {
	int i = 0;
	HYN_FUNC_ENTER();

	HYN_INFO("hyn_find_fw_idx check_project_id:%d, chip_ic_type :0x%x, "
		 "chip_ic_project_id:0x%x, config_chip_type :0x%x.\r\n",
		 check_project_id, hyn_ts_data->chip_ic_type,
		 hyn_ts_data->chip_ic_project_id,
		 hyn_ts_data->config_chip_type);

	if (0 != hyn_ts_data->config_chip_type) {
		for (i = 0; i < ARRAY_SIZE(hynitron_fw_grp); i++) {
			if (check_project_id == 1) {
				if ((hyn_ts_data->chip_ic_project_id ==
				     hynitron_fw_grp[i].project_id) &&
				    (hyn_ts_data->chip_ic_type ==
				     hynitron_fw_grp[i].chip_type)) {
					return i;
				}
			} else {
				if (hyn_ts_data->config_chip_type ==
				    hynitron_fw_grp[i].chip_type) {
					return i;
				}
			}
		}
	}
	HYN_FUNC_EXIT();
	return -1;
}
/*******************************************************
Function:
	get firmware version, ic type...
Input:
	client: i2c client
Output:
	success: 0
	fail:	-1
*******************************************************/
int hyn_firmware_info(struct i2c_client *client) {
	int ret = -1;
	unsigned char retry = 0;
	HYN_FUNC_ENTER();

	if (hyn_ts_data->config_chip_product_line ==
	    HYN_CHIP_PRODUCT_LINE_MUT_CAP) {
		// mutcap
		for (retry = 0; retry < 3; retry++) {
			ret = cst3xx_firmware_info(client);
			if (ret < 0) {
				HYN_INFO("cst3xx_firmware_info read fail, "
					 "retry=%d.\r\n",
					 retry);
			} else {
				break;
			}
		}

	} else if (hyn_ts_data->config_chip_product_line ==
		   HYN_CHIP_PRODUCT_LINE_SEL_CAP) {
		// selfcap
		for (retry = 0; retry < 3; retry++) {
			ret = cst8xx_firmware_info(client);
			if (ret < 0) {
				HYN_INFO("cst8xx_firmware_info read fail, "
					 "retry=%d.\r\n",
					 retry);
			} else {
				break;
			}
		}
	}
	HYN_FUNC_EXIT();
	return ret;
}

int hyn_boot_update_fw(struct i2c_client *client) {
	int ret = -1;
	int proj_id = 0;
	int check_proj_id = 1;

	HYN_FUNC_ENTER();

	if (hyn_ts_data->apk_upgrade_flag == 0) {

		if (hyn_ts_data->chip_ic_checkcode == 0xffffffff) {
			HYN_ERROR("hyn ic have no firmware, update default "
				  "firmware.\n");
			check_proj_id = 0;
			proj_id = hyn_find_fw_idx(check_proj_id);
			HYN_INFO("hyn_find_match chip type, fw_idx: "
				 "proj_id:%d.\r\n",
				 proj_id);
			if (proj_id < 0) {
				HYN_ERROR("hyn not find config chip type "
					  "firmware, please check config "
					  "chip type or firmware.\n");
				proj_id = hyn_find_fw_idx(0);
				if (proj_id > 0) {
					hyn_ts_data->fw_name =
						hynitron_fw_grp[proj_id].name;
					hyn_ts_data
						->p_hynitron_upgrade_firmware =
						hynitron_fw_grp[proj_id].fw;
					hyn_ts_data->fw_length =
						hynitron_fw_grp[proj_id]
							.fw_length;
					HYN_INFO("update default TP firmware "
						 "data for apk update.(%s)!\n",
						 hynitron_fw_grp[proj_id].name);
				} else {
					HYN_INFO("please chekc hynitron_fw_grp "
						 "fw config!\n");
					return 0;
				}
			} else {
				hyn_ts_data->fw_name =
					hynitron_fw_grp[proj_id].name;
				hyn_ts_data->p_hynitron_upgrade_firmware =
					hynitron_fw_grp[proj_id].fw;
				hyn_ts_data->fw_length =
					hynitron_fw_grp[proj_id].fw_length;
				HYN_INFO("find default TP firmware(%s)!\n",
					 hynitron_fw_grp[proj_id].name);
			}

		} else {
			check_proj_id = 1;
			proj_id = hyn_find_fw_idx(check_proj_id);
			HYN_INFO("hyn_find_fw_idx: proj_id:%d.\r\n", proj_id);
			if (proj_id < 0) {
				HYN_ERROR("not find matched TP firmware, "
					  "please check chip type and "
					  "chip project id. !\n");
				proj_id = hyn_find_fw_idx(0);
				if (proj_id > 0) {
					hyn_ts_data->fw_name =
						hynitron_fw_grp[proj_id].name;
					hyn_ts_data
						->p_hynitron_upgrade_firmware =
						hynitron_fw_grp[proj_id].fw;
					hyn_ts_data->fw_length =
						hynitron_fw_grp[proj_id]
							.fw_length;
					HYN_INFO("update default TP firmware "
						 "data for apk update.(%s)!\n",
						 hynitron_fw_grp[proj_id].name);
				} else {
					HYN_INFO("please chekc hynitron_fw_grp "
						 "fw config!\n");
					return 0;
				}
			} else {
				HYN_INFO("find matched TP firmware(%s)!\n",
					 hynitron_fw_grp[proj_id].name);
				hyn_ts_data->fw_name =
					hynitron_fw_grp[proj_id].name;
				hyn_ts_data->p_hynitron_upgrade_firmware =
					hynitron_fw_grp[proj_id].fw;
				hyn_ts_data->fw_length =
					hynitron_fw_grp[proj_id].fw_length;
			}
		}
	}

	if (hyn_ts_data->p_hynitron_upgrade_firmware == NULL) {
		HYN_ERROR(
			"hyn_ts_data->p_hynitron_upgrade_firmware is NULL .\n");
		return 0;
	}

#if HYN_ESDCHECK_EN
	hyn_esd_suspend();
#endif

	hyn_irq_disable();

	hyn_ts_data->work_mode = HYN_WORK_MODE_UPDATE; // add work mode

	switch (hyn_ts_data->config_chip_series) {
	case HYN_CHIP_CST0XX:
		break;
	case HYN_CHIP_CST92XX:
#if HYN_EN_AUTO_UPDATE_CST92XX
		ret = cst92xx_boot_update_fw(client);
#endif
		break;
	case HYN_CHIP_CST6XX:
#if (HYN_EN_AUTO_UPDATE_CST6xx)
		ret = cst6xx_boot_update_fw(client);
#endif
		break;
	case HYN_CHIP_CST1XX:
	case HYN_CHIP_CST3XX:
	case HYN_CHIP_CST9XX:
#if (HYN_EN_AUTO_UPDATE_CST1xx || HYN_EN_AUTO_UPDATE_CST3xx ||                 \
     HYN_EN_AUTO_UPDATE_CST9xx)
		ret = cst3xx_boot_update_fw(client);
#endif
		break;

	case HYN_CHIP_CST3240:
#if (HYN_EN_AUTO_UPDATE_CST3240)
		ret = cst3240_boot_update_fw(client);
#endif
		break;

	case HYN_CHIP_CST1XXSE:
	case HYN_CHIP_CST2XXSE:
	case HYN_CHIP_CST3XXSE:
#if (HYN_EN_AUTO_UPDATE_CST1xxSE || HYN_EN_AUTO_UPDATE_CST2xxSE ||             \
     HYN_EN_AUTO_UPDATE_CST3xxSE)
		ret = cst3xxse_boot_update_fw(client);
#endif
		break;
	case HYN_CHIP_CST7XX:
	case HYN_CHIP_CST8XX:
#if HYN_EN_AUTO_UPDATE_CST78xx
		ret = cst8xx_boot_update_fw(client);
#endif
		break;
	case HYN_CHIP_CST644K:
#if HYN_EN_AUTO_UPDATE_CST644K
		ret = cst644k_boot_update_fw(client);
#endif
		break;
	default:
		break;
	}

	hyn_irq_enable();

#if HYN_ESDCHECK_EN
	hyn_esd_resume();
#endif

	hyn_ts_data->work_mode = HYN_WORK_MODE_NORMAL;
	// add work mode

	HYN_FUNC_EXIT();
	return ret;
}

static int cst0xx_bootloader_enter(struct i2c_client *client) {
	HYN_FUNC_ENTER();
	HYN_FUNC_EXIT();
	return 0;
}

static int cst2xx_bootloader_enter(struct i2c_client *client) {

	int ret = -1;
	u8 retry;
	unsigned char buf[4];
	HYN_FUNC_ENTER();

	for (retry = 0; retry < 5; retry++) {

		hyn_reset_proc(7 + retry);
		if (hyn_ts_data->chip_ic_bootloader_addr != client->addr) {
			client->addr = hyn_ts_data->chip_ic_bootloader_addr;
		}
		HYN_DEBUG(" bootloader mode client->addr:0x%x.\r\n",
			  client->addr);
		buf[0] = 0xD1;
		buf[1] = 0x11;
		ret = cst3xx_i2c_write(hyn_ts_data->client, buf, 2);
		if (ret < 0)
			continue;

		mdelay(1); // wait enter download mode

		buf[0] = 0xD0;
		buf[1] = 0x01;
		ret = cst3xx_i2c_read_register(hyn_ts_data->client, buf, 1);
		if (ret < 0)
			continue;
		if (buf[0] == 0x55) {
			HYN_DEBUG("[HYN]receive 0x55 success.\r\n");
			break;
		}
	}
	buf[0] = 0xD1;
	buf[1] = 0x10; // enter writer register mode
	ret = cst3xx_i2c_write(hyn_ts_data->client, buf, 2);
	if (ret < 0) {
		HYN_DEBUG("[HYN]Send cmd 0xD110 failed. \r\n");
		return -1;
	}
	HYN_FUNC_EXIT();
	return 0;
}

static int cst3xx_bootloader_enter(struct i2c_client *client) {
	int ret = -1;
	u8 retry;
	unsigned char buf[4];

	HYN_FUNC_ENTER();
	for (retry = 0; retry < 10; retry++) {

		hyn_reset_proc(7 + retry);
		if (hyn_ts_data->chip_ic_bootloader_addr != client->addr) {
			client->addr = hyn_ts_data->chip_ic_bootloader_addr;
		}
		HYN_DEBUG(" bootloader mode client->addr:0x%x.\r\n",
			  client->addr);
		buf[0] = 0xA0;
		buf[1] = 0x01;
		buf[2] = 0xAA; // set cmd to enter program mode
		ret = cst3xx_i2c_write(client, buf, 3);
		if (ret < 0)
			continue;
		mdelay(2);

		buf[0] = 0xA0;
		buf[1] = 0x02; // check whether into program mode
		ret = cst3xx_i2c_read_register(client, buf, 1);
		if (ret < 0)
			continue;

		if (hyn_ts_data->config_chip_series == HYN_CHIP_CST3XX) {
			if (buf[0] == 0xAC)
				break;

		} else {
			if (buf[0] == 0x55)
				break;
		}
	}

	if (hyn_ts_data->config_chip_series == HYN_CHIP_CST3XX) {
		if (buf[0] != 0xAC) {
			HYN_ERROR("cst3xx_bootloader_enter error, please check "
				  "VCC/RST/SDA/SCL "
				  "and chip type.\r\n");
			return -1;
		} else {
			HYN_DEBUG("cst3xx_bootloader_enter detect success, "
				  "buf[0]=%d.\n",
				  buf[0]);
		}

	} else {
		if (buf[0] != 0x55) {
			HYN_ERROR("cst1xx_bootloader_enter error, please check "
				  "VCC/RST/SDA/SCL "
				  "and chip type.\r\n");
			return -1;
		} else {
			HYN_DEBUG("cst1xx_bootloader_enter detect success, "
				  "buf[0]=%d.\n",
				  buf[0]);
		}
	}

	HYN_FUNC_EXIT();
	return 0;
}

static int cst3240_bootloader_enter(struct i2c_client *client) {
	int ret = -1;
	u8 retry;
	unsigned char buf[4];

	HYN_FUNC_ENTER();
	for (retry = 0; retry < 10; retry++) {

		hyn_reset_proc(7 + retry);
		if (hyn_ts_data->chip_ic_bootloader_addr != client->addr) {
			client->addr = hyn_ts_data->chip_ic_bootloader_addr;
		}
		HYN_DEBUG(" bootloader mode client->addr:0x%x.\r\n",
			  client->addr);
		buf[0] = 0xA0;
		buf[1] = 0x01;
		buf[2] = 0xAA; // set cmd to enter program mode
		ret = cst3xx_i2c_write(client, buf, 3);
		if (ret < 0)
			continue;
		mdelay(2);

		buf[0] = 0xA0;
		buf[1] = 0x02; // check whether into program mode
		ret = cst3xx_i2c_read_register(client, buf, 1);
		if (ret < 0)
			continue;

		if (buf[0] == 0x55)
			break;
	}

	if (buf[0] != 0x55) {
		HYN_ERROR("cst32xx_bootloader_enter error, please check "
			  "VCC/RST/SDA/SCL "
			  "and chip type.\r\n");
		return -1;
	} else {
		HYN_DEBUG(
			"cst32xx_bootloader_enter detect success, buf[0]=%d.\n",
			buf[0]);
	}

	mdelay(2);
	buf[0] = 0xA0;
	buf[1] = 0x03; // check whether into program mode
	ret = cst3xx_i2c_read_register(client, buf, 3);
	if (ret < 0)
		return -1;

	if (buf[0] != 0xA8) {
		HYN_DEBUG("cst32xx_bootloader_enter detect 0xA8, buf[0]=%d.\n",
			  buf[0]);
		return -1;
	}

	mdelay(2);
	buf[0] = 0xA0;
	buf[1] = 0x01;
	buf[2] = 0x00;
	ret = cst3xx_i2c_write(client, buf, 3);
	if (ret < 0)
		return -1;

	HYN_FUNC_EXIT();
	return 0;
}

static int cst92xx_bootloader_enter(struct i2c_client *client) {
	int ret = -1;
	u8 retry;
	unsigned char buf[4];

	HYN_FUNC_ENTER();
	for (retry = 0; retry < 10; retry++) {

		hyn_reset_proc(7 + retry);
		if (hyn_ts_data->chip_ic_bootloader_addr != client->addr) {
			client->addr = hyn_ts_data->chip_ic_bootloader_addr;
		}
		HYN_DEBUG(" bootloader mode client->addr:0x%x.\r\n",
			  client->addr);
		buf[0] = 0xA0;
		buf[1] = 0x01;
		buf[2] = 0xAA; // set cmd to enter program mode
		ret = cst3xx_i2c_write(client, buf, 3);
		if (ret < 0)
			continue;
		mdelay(2);

		buf[0] = 0xA0;
		buf[1] = 0x02; // check whether into program mode
		ret = cst3xx_i2c_read_register(client, buf, 2);
		if (ret < 0)
			continue;

		if ((buf[0] == 0x55) && (buf[1] == 0xB0))
			break;
	}

	if (retry == 10) {
		HYN_ERROR("cst92xx_bootloader_enter error, please check "
			  "VCC/RST/SDA/SCL "
			  "and chip type.\r\n");
		return -1;
	} else {
		HYN_DEBUG("cst92xx_bootloader_enter detect success, "
			  "buf[0]=0x%x, buf[1]=0x%x.\n",
			  buf[0], buf[1]);
	}

	HYN_FUNC_EXIT();
	return 0;
}

static int cst6xx_bootloader_enter(struct i2c_client *client) {
	int ret = -1;
	u8 retry;
	unsigned char buf[4];

	HYN_FUNC_ENTER();
	for (retry = 0; retry < 10; retry++) {

		hyn_reset_proc(7 + retry);
		if (hyn_ts_data->chip_ic_bootloader_addr != client->addr) {
			client->addr = hyn_ts_data->chip_ic_bootloader_addr;
		}
		HYN_DEBUG(" bootloader mode client->addr:0x%x.\r\n",
			  client->addr);
		buf[0] = 0xA0;
		buf[1] = 0x01;
		buf[2] = 0xAB; // set cmd to enter program mode
		ret = cst3xx_i2c_write(client, buf, 3);
		if (ret < 0)
			continue;
		mdelay(2);

		buf[0] = 0xA0;
		buf[1] = 0x02; // check whether into program mode
		ret = cst3xx_i2c_read_register(client, buf, 1);
		if (ret < 0)
			continue;

		if (buf[0] == 0x55)
			break;
	}

	if (buf[0] != 0x55) {
		HYN_ERROR("cst6xx_bootloader_enter error, please check "
			  "VCC/RST/SDA/SCL and "
			  "chip type.\r\n");
		ret = -1;
	} else {
		HYN_DEBUG("cst6xx_bootloader_enter detect success, "
			  "buf[0]=0x%x.\n",
			  buf[0]);
		ret = 0;
	}

	HYN_FUNC_EXIT();
	return ret;
}

static int cst3xxse_bootloader_enter(struct i2c_client *client) {
	int ret = -1;
	u8 retry;
	unsigned char buf[4];

	HYN_FUNC_ENTER();
	for (retry = 0; retry < 5; retry++) {

		hyn_reset_proc(7 + retry);
		if (hyn_ts_data->chip_ic_bootloader_addr != client->addr) {
			client->addr = hyn_ts_data->chip_ic_bootloader_addr;
		}
		HYN_DEBUG(" bootloader mode client->addr:0x%x.\r\n",
			  client->addr);
		buf[0] = 0xA0;
		buf[1] = 0x01;
		buf[2] = 0xAA; // set cmd to enter program mode
		ret = cst3xx_i2c_write(client, buf, 3);
		if (ret < 0)
			continue;
		mdelay(5);

		buf[0] = 0xA0;
		buf[1] = 0x03; // check whether into program mode
		ret = cst3xx_i2c_read_register(client, buf, 1);
		if (ret < 0)
			continue;

		if (buf[0] == 0x55)
			break;
	}
	if (buf[0] != 0x55) {
		HYN_ERROR("cst3xx_bootloader_enter error, please check "
			  "VCC/RST/SDA/SCL and "
			  "chip type.\r\n");
		return -1;
	} else {
		HYN_DEBUG(
			"cst3xx_bootloader_enter detect success, buf[0]=%d.\n",
			buf[0]);
	}

	HYN_FUNC_EXIT();
	return 0;
}
static int cst8xx_bootloader_enter(struct i2c_client *client) {
	char retryCnt = 10;
	HYN_FUNC_ENTER();

	hyn_reset_proc(1);
	mdelay(5);
	if (hyn_ts_data->chip_ic_bootloader_addr != client->addr) {
		client->addr = hyn_ts_data->chip_ic_bootloader_addr;
	}
	HYN_DEBUG(" bootloader mode client->addr:0x%x.\r\n", client->addr);
	while (retryCnt--) {
		u8 cmd[3];
		cmd[0] = 0xAA;
		if (-1 ==
		    hyn_i2c_write_bytes(0xA001, cmd, 1,
					REG_LEN_2B)) { // enter program mode
			mdelay(2);		       // 4ms
			continue;
		}
		if (-1 == hyn_i2c_read_bytes(0xA003, cmd, 1,
					     REG_LEN_2B)) { // read flag
			mdelay(2);			    // 4ms
			continue;
		} else {
			if (cmd[0] != 0x55) {
				mdelay(2); // 4ms
				continue;
			} else {
				HYN_DEBUG("cst8xx_bootloader_enter detect "
					  "success, cmd[0]=%d.\n",
					  cmd[0]);
				return 0;
			}
		}
	}

	HYN_FUNC_EXIT();
	return -1;
}

// 201
static int cst644K_bootloader_enter(struct i2c_client *client) {
	int ret = -1;
	u8 retry;
	unsigned char buf[4];

	HYN_FUNC_ENTER();
	for (retry = 0; retry < 10; retry++) {

		hyn_reset_proc(7 + retry);
		if (hyn_ts_data->chip_ic_bootloader_addr != client->addr) {
			client->addr = hyn_ts_data->chip_ic_bootloader_addr;
		}
		HYN_DEBUG(" bootloader mode client->addr:0x%x.\r\n",
			  client->addr);
		buf[0] = 0xA0;
		buf[1] = 0x01;
		buf[2] = 0xAA; // set cmd to enter program mode
		ret = cst3xx_i2c_write(client, buf, 3);
		if (ret < 0)
			continue;
		mdelay(2);

		buf[0] = 0xA0;
		buf[1] = 0x02; // check whether into program mode
		ret = cst3xx_i2c_read_register(client, buf, 1);
		if (ret < 0)
			continue;

		if (buf[0] == 0x55)
			break;
	}

	if (buf[0] != 0x55) {
		HYN_ERROR("cst644K_bootloader_enter error, please check "
			  "VCC/RST/SDA/SCL "
			  "and chip type.\r\n");
		return -1;
	} else {
		HYN_DEBUG("cst644K_bootloader_enter detect success, "
			  "buf[0]=0x%x.\n",
			  buf[0]);
	}

	HYN_FUNC_EXIT();
	return 0;
}
int hyn_detect_bootloader(struct i2c_client *client) {
	int ret = -1;

	switch (hyn_ts_data->config_chip_series) {
	case HYN_CHIP_CST0XX:
		ret = cst0xx_bootloader_enter(client);
		break;
	case HYN_CHIP_CST2XX:
		ret = cst2xx_bootloader_enter(client);
		break;
	case HYN_CHIP_CST6XX:
		ret = cst6xx_bootloader_enter(client);
		break;
	case HYN_CHIP_CST1XX:
	case HYN_CHIP_CST3XX:
		ret = cst3xx_bootloader_enter(client);
		break;
	case HYN_CHIP_CST3240:
		ret = cst3240_bootloader_enter(client);
		break;
	case HYN_CHIP_CST92XX:
		ret = cst92xx_bootloader_enter(client);
		break;
	case HYN_CHIP_CST1XXSE:
	case HYN_CHIP_CST2XXSE:
	case HYN_CHIP_CST3XXSE:
		ret = cst3xxse_bootloader_enter(client);
		break;
	case HYN_CHIP_CST7XX:
	case HYN_CHIP_CST8XX:
		ret = cst8xx_bootloader_enter(client);
		break;
	case HYN_CHIP_CST644K:
		ret = cst644K_bootloader_enter(client);
		break;
	default:
		break;
	}
	if (hyn_ts_data->chip_ic_main_addr != client->addr) {
		client->addr = hyn_ts_data->chip_ic_main_addr;
	}
	HYN_DEBUG("exit bootloader mode, client->addr:0x%x.\r\n", client->addr);
	hyn_reset_proc(40);
	HYN_FUNC_EXIT();
	return ret;
}
